mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-12-28 17:13:45 +03:00
Merge branch 'master' into chrome31
Conflicts: package.json vendor/apm
This commit is contained in:
commit
fcffcc83f9
14
atom.gyp
14
atom.gyp
@ -1,14 +0,0 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'Atom',
|
||||
'type': 'none',
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Create Atom, basically do everything',
|
||||
'action': ['script/constructicon/build'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -151,8 +151,10 @@ module.exports = (grunt) ->
|
||||
'dot-atom/**/*.coffee'
|
||||
'exports/**/*.coffee'
|
||||
'src/**/*.coffee'
|
||||
'tasks/**/*.coffee'
|
||||
'Gruntfile.coffee'
|
||||
]
|
||||
build: [
|
||||
'build/tasks/**/*.coffee'
|
||||
'build/Gruntfile.coffee'
|
||||
]
|
||||
test: [
|
||||
'spec/*.coffee'
|
||||
@ -225,7 +227,6 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-development-version', 'lint', 'test', 'publish-build'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'download-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-version', 'lint', 'test', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-version', 'install'])
|
||||
|
@ -39,19 +39,27 @@ module.exports = (grunt) ->
|
||||
else
|
||||
nonPackageDirectories.push(directory)
|
||||
|
||||
# Put any paths here that shouldn't end up in the built Atom.app
|
||||
# so that it doesn't becomes larger than it needs to be.
|
||||
ignoredPaths = [
|
||||
path.join('git-utils', 'deps')
|
||||
path.join('oniguruma', 'deps')
|
||||
path.join('less', 'dist')
|
||||
path.join('less', 'test')
|
||||
path.join('bootstrap', 'docs')
|
||||
path.join('spellchecker', 'vendor')
|
||||
path.join('xmldom', 'test')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('resources', 'mac')
|
||||
path.join('resources', 'win')
|
||||
]
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
|
||||
for directory in nonPackageDirectories
|
||||
cp directory, path.join(appDir, directory), filter: nodeModulesFilter
|
||||
for directory in packageDirectories
|
||||
cp directory, path.join(appDir, directory), filter: /.+\.(cson|coffee)$/
|
||||
cp directory, path.join(appDir, directory), filter: packageFilter
|
||||
|
||||
cp 'spec', path.join(appDir, 'spec')
|
||||
cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/
|
||||
|
@ -3,6 +3,23 @@ module.exports = (grunt) ->
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
done = @async()
|
||||
|
||||
if process.env.XCODE_KEYCHAIN
|
||||
unlockKeychain (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
signApp(done)
|
||||
else
|
||||
signApp(done)
|
||||
|
||||
unlockKeychain = (callback) ->
|
||||
cmd = 'security'
|
||||
{XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env
|
||||
args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
|
||||
signApp = (callback) ->
|
||||
cmd = 'codesign'
|
||||
args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> done(error)
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
|
@ -24,13 +24,19 @@ module.exports = (gruntObject) ->
|
||||
|
||||
done = @async()
|
||||
|
||||
createRelease (error, release) ->
|
||||
createBuildRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
zipApp (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
publishRelease(release, done)
|
||||
publishRelease release, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
deleteExistingAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset(release, done)
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
@ -65,6 +71,18 @@ getRelease = (callback) ->
|
||||
return
|
||||
callback()
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
if error?
|
||||
logError('Fetching atom/atom releases failed', error, releases)
|
||||
callback(error)
|
||||
else
|
||||
for release in releases when release.draft
|
||||
callback(null, release)
|
||||
return
|
||||
callback(new Error('No draft release in atom/atom repo'))
|
||||
|
||||
deleteRelease = (release) ->
|
||||
options =
|
||||
uri: release.url
|
||||
@ -92,7 +110,7 @@ deleteExistingAsset = (release, callback) ->
|
||||
|
||||
callback()
|
||||
|
||||
createRelease = (callback) ->
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
@ -123,7 +141,7 @@ createRelease = (callback) ->
|
||||
|
||||
uploadAsset = (release, callback) ->
|
||||
options =
|
||||
uri: "https://uploads.github.com/repos/atom/atom-master-builds/releases/#{release.id}/assets?name=#{assetName}"
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
|
@ -4,15 +4,24 @@ path = require 'path'
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'set-development-version', 'Sets version to current SHA-1', ->
|
||||
getVersion = (callback) ->
|
||||
if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
callback(null, version)
|
||||
else
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
callback(error, stdout?.trim?())
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, result, code) ->
|
||||
return done(error) if error?
|
||||
getVersion (error, version) ->
|
||||
if error?
|
||||
done(error)
|
||||
return
|
||||
|
||||
version = result.stdout.trim()
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
# Replace version field of package.json.
|
||||
@ -32,7 +41,7 @@ module.exports = (grunt) ->
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'The hackable, collaborative editor of tomorrow!'
|
||||
FileDescription: 'The hackable, collaborative editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
59
package.json
59
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.45.0",
|
||||
"version": "0.46.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -19,7 +19,7 @@
|
||||
"atomShellVersion": "0.8.5",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/benogle/bootstrap.git",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.2.0",
|
||||
"coffee-script": "1.6.3",
|
||||
"coffeestack": "0.6.0",
|
||||
@ -28,11 +28,9 @@
|
||||
"fs-plus": "0.14.0",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "0.6.0",
|
||||
"git-utils": "0.30.0",
|
||||
"git-utils": "0.33.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"jasmine-node": "git://github.com/kevinsawicki/jasmine-node.git#short-stacks",
|
||||
"jasmine-tagged": "0.2.0",
|
||||
"jasmine-tagged": "0.3.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"keytar": "0.15.0",
|
||||
"less-cache": "0.10.0",
|
||||
@ -43,17 +41,18 @@
|
||||
"pathwatcher": "0.14.0",
|
||||
"pegjs": "0.8.0",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.11.0",
|
||||
"scandal": "0.12.0",
|
||||
"season": "0.14.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "3.1.0",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": "0.12.0",
|
||||
"text-buffer": "0.13.0",
|
||||
"underscore-plus": "0.6.1",
|
||||
"vm-compatibility-layer": "0.1.0",
|
||||
"theorist": "~0.13.0",
|
||||
"delegato": "~0.4.0",
|
||||
"mixto": "~0.4.0"
|
||||
"mixto": "~0.4.0",
|
||||
"property-accessors": "~0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.10.0",
|
||||
@ -64,46 +63,46 @@
|
||||
"solarized-dark-syntax": "0.6.0",
|
||||
"solarized-light-syntax": "0.2.0",
|
||||
"archive-view": "0.19.0",
|
||||
"autocomplete": "0.19.0",
|
||||
"autoflow": "0.11.0",
|
||||
"autocomplete": "0.20.0",
|
||||
"autoflow": "0.12.0",
|
||||
"autosave": "0.10.0",
|
||||
"background-tips": "0.4.0",
|
||||
"bookmarks": "0.15.0",
|
||||
"bracket-matcher": "0.16.0",
|
||||
"bookmarks": "0.16.0",
|
||||
"bracket-matcher": "0.19.0",
|
||||
"command-logger": "0.9.0",
|
||||
"command-palette": "0.14.0",
|
||||
"dev-live-reload": "0.22.0",
|
||||
"editor-stats": "0.12.0",
|
||||
"exception-reporting": "0.11.0",
|
||||
"feedback": "0.22.0",
|
||||
"find-and-replace": "0.74.0",
|
||||
"fuzzy-finder": "0.30.0",
|
||||
"gists": "0.14.0",
|
||||
"git-diff": "0.21.0",
|
||||
"find-and-replace": "0.75.0",
|
||||
"fuzzy-finder": "0.31.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.22.0",
|
||||
"github-sign-in": "0.16.0",
|
||||
"go-to-line": "0.14.0",
|
||||
"grammar-selector": "0.16.0",
|
||||
"go-to-line": "0.15.0",
|
||||
"grammar-selector": "0.17.0",
|
||||
"image-view": "0.15.0",
|
||||
"keybinding-resolver": "0.8.0",
|
||||
"markdown-preview": "0.24.0",
|
||||
"metrics": "0.21.0",
|
||||
"package-generator": "0.23.0",
|
||||
"package-generator": "0.24.0",
|
||||
"release-notes": "0.15.0",
|
||||
"settings-view": "0.55.0",
|
||||
"snippets": "0.18.0",
|
||||
"spell-check": "0.19.0",
|
||||
"status-bar": "0.31.0",
|
||||
"settings-view": "0.56.0",
|
||||
"snippets": "0.19.0",
|
||||
"spell-check": "0.20.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.19.0",
|
||||
"symbols-view": "0.28.0",
|
||||
"symbols-view": "0.29.0",
|
||||
"tabs": "0.17.0",
|
||||
"terminal": "0.24.0",
|
||||
"timecop": "0.13.0",
|
||||
"to-the-hubs": "0.17.0",
|
||||
"tree-view": "0.59.0",
|
||||
"tree-view": "0.61.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.11.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.4.0",
|
||||
@ -122,12 +121,12 @@
|
||||
"language-objective-c": "0.2.0",
|
||||
"language-pegjs": "0.1.0",
|
||||
"language-perl": "0.2.0",
|
||||
"language-php": "0.2.0",
|
||||
"language-php": "0.3.0",
|
||||
"language-property-list": "0.2.0",
|
||||
"language-puppet": "0.2.0",
|
||||
"language-python": "0.2.0",
|
||||
"language-ruby": "0.6.0",
|
||||
"language-ruby-on-rails": "0.3.0",
|
||||
"language-ruby": "0.7.0",
|
||||
"language-ruby-on-rails": "0.4.0",
|
||||
"language-sass": "0.3.0",
|
||||
"language-shellscript": "0.2.0",
|
||||
"language-source": "0.2.0",
|
||||
|
@ -31,6 +31,7 @@ if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
var packagesToDedupe = ['nan', 'oniguruma', 'roaster'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
@ -43,6 +44,7 @@ var commands = [
|
||||
echoNewLine,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm clean ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
@ -10,11 +10,9 @@ if (process.platform == 'linux')
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
var credentialsPath = '/var/lib/jenkins/config/atomcredentials';
|
||||
function loadEnvironmentVariables(filePath) {
|
||||
try {
|
||||
var credentials = fs.readFileSync(credentialsPath, 'utf8');
|
||||
var lines = credentials.trim().split('\n');
|
||||
var lines = fs.readFileSync(filePath, 'utf8').trim().split('\n');
|
||||
for (i in lines) {
|
||||
var parts = lines[i].split('=');
|
||||
var key = parts[0].trim();
|
||||
@ -24,6 +22,11 @@ function readEnvironmentVariables() {
|
||||
} catch(error) { }
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials')
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain')
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
|
@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
# This entire file is a hack so that constructicon can build Atom via
|
||||
# xcode
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
rm -fr node_modules
|
||||
rm -fr vendor/apm/node_modules
|
||||
./script/bootstrap --no-color
|
||||
./build/node_modules/.bin/grunt --no-color --build-dir="$BUILT_PRODUCTS_DIR" deploy
|
||||
|
||||
echo "TARGET_BUILD_DIR=$BUILT_PRODUCTS_DIR"
|
||||
echo "FULL_PRODUCT_NAME=Atom.app"
|
||||
echo "PRODUCT_NAME=Atom"
|
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
cd "$(dirname "$0")/../.."
|
||||
export PATH="atom-shell/Atom.app/Contents/Resources/:${PATH}"
|
||||
|
||||
rm -rf atom.xcodeproj
|
||||
gyp --depth=. atom.gyp
|
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
{$, $$} = require 'atom'
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
|
||||
require 'jasmine-focused'
|
||||
require 'jasmine-tagged'
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
|
||||
TimeReporter = require './time-reporter'
|
||||
timeReporter = new TimeReporter()
|
||||
@ -18,8 +17,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
process.stderr.write(str)
|
||||
|
||||
if atom.getLoadSettings().exitWhenDone
|
||||
{jasmineNode} = require 'jasmine-node/lib/jasmine-node/reporter'
|
||||
reporter = new jasmineNode.TerminalReporter
|
||||
reporter = new TerminalReporter
|
||||
print: (str) ->
|
||||
log(str)
|
||||
onComplete: (runner) ->
|
||||
|
@ -42,7 +42,7 @@ describe "PaneContainerView", ->
|
||||
describe ".focusPreviousPane()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
$(document.body).focus() # clear focus
|
||||
container.getPanes()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
@ -121,7 +121,7 @@ describe "PaneContainerView", ->
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist()
|
||||
@ -133,7 +133,7 @@ describe "PaneContainerView", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row, .pane-column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
|
@ -1,134 +0,0 @@
|
||||
{Model} = require 'theorist'
|
||||
Pane = require '../src/pane'
|
||||
PaneAxis = require '../src/pane-axis'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
|
||||
describe "Pane", ->
|
||||
describe "split methods", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: ["A"])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
describe "::splitLeft(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "::splitRight(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "::splitUp(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "::splitDown(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
it "sets up the new pane to be focused", ->
|
||||
expect(pane1.focused).toBe false
|
||||
pane2 = pane1.splitRight()
|
||||
expect(pane2.focused).toBe true
|
||||
|
||||
describe "::destroyItem(item)", ->
|
||||
describe "when the last item is destroyed", ->
|
||||
it "destroys the pane", ->
|
||||
pane = new Pane(items: ["A", "B"])
|
||||
pane.destroyItem("A")
|
||||
pane.destroyItem("B")
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
|
||||
describe "when an item emits a destroyed event", ->
|
||||
it "removes it from the list of items", ->
|
||||
pane = new Pane(items: [new Model, new Model, new Model])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.items[1].destroy()
|
||||
expect(pane.items).toEqual [item1, item3]
|
||||
|
||||
describe "::destroy()", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: [new Model, new Model])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
it "destroys the pane's destroyable items", ->
|
||||
[item1, item2] = pane1.items
|
||||
pane1.destroy()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
|
||||
describe "if the pane's parent has more than two children", ->
|
||||
it "removes the pane from its parent", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
|
||||
expect(container.root.children).toEqual [pane1, pane2, pane3]
|
||||
pane2.destroy()
|
||||
expect(container.root.children).toEqual [pane1, pane3]
|
||||
|
||||
describe "if the pane's parent has two children", ->
|
||||
it "replaces the parent with its last remaining child", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
|
||||
expect(container.root.children[0]).toBe pane1
|
||||
expect(container.root.children[1].children).toEqual [pane2, pane3]
|
||||
pane3.destroy()
|
||||
expect(container.root.children).toEqual [pane1, pane2]
|
||||
pane2.destroy()
|
||||
expect(container.root).toBe pane1
|
437
spec/pane-spec.coffee
Normal file
437
spec/pane-spec.coffee
Normal file
@ -0,0 +1,437 @@
|
||||
{Model} = require 'theorist'
|
||||
Pane = require '../src/pane'
|
||||
PaneAxis = require '../src/pane-axis'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
|
||||
describe "Pane", ->
|
||||
class Item extends Model
|
||||
@deserialize: ({name, uri}) -> new this(name, uri)
|
||||
constructor: (@name, @uri) ->
|
||||
getUri: -> @uri
|
||||
getPath: -> @path
|
||||
serialize: -> {deserializer: 'Item', @name, @uri}
|
||||
isEqual: (other) -> @name is other?.name
|
||||
|
||||
beforeEach ->
|
||||
atom.deserializers.add(Item)
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(Item)
|
||||
|
||||
describe "construction", ->
|
||||
it "sets the active item to the first item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B")])
|
||||
|
||||
it "changes the active item to the current item", ->
|
||||
expect(pane.activeItem).toBe pane.items[0]
|
||||
pane.activateItem(pane.items[1])
|
||||
expect(pane.activeItem).toBe pane.items[1]
|
||||
|
||||
it "adds the given item if it isn't present in ::items", ->
|
||||
item = new Item("C")
|
||||
pane.activateItem(item)
|
||||
expect(item in pane.items).toBe true
|
||||
expect(pane.activeItem).toBe item
|
||||
|
||||
describe "::activateNextItem() and ::activatePreviousItem()", ->
|
||||
it "sets the active item to the next/previous item, looping around at either end", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.activeItem).toBe item2
|
||||
pane.activateNextItem()
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activateNextItem()
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
describe "::activateItemAtIndex(index)", ->
|
||||
it "activates the item at the given index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.activateItemAtIndex(2)
|
||||
expect(pane.activeItem).toBe item3
|
||||
pane.activateItemAtIndex(1)
|
||||
expect(pane.activeItem).toBe item2
|
||||
pane.activateItemAtIndex(0)
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
# Doesn't fail with out-of-bounds indices
|
||||
pane.activateItemAtIndex(100)
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.activateItemAtIndex(-1)
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
describe "::destroyItem(item)", ->
|
||||
[pane, item1, item2, item3] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
|
||||
it "removes the item from the items list and activates the next item if it was the active item", ->
|
||||
expect(pane.activeItem).toBe item1
|
||||
pane.destroyItem(item2)
|
||||
expect(item2 in pane.items).toBe false
|
||||
expect(pane.activeItem).toBe item1
|
||||
|
||||
pane.destroyItem(item1)
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(pane.activeItem).toBe item3
|
||||
|
||||
it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", ->
|
||||
pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.destroyItem(item2)
|
||||
expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true)
|
||||
|
||||
describe "if the item is modified", ->
|
||||
itemUri = null
|
||||
|
||||
beforeEach ->
|
||||
item1.shouldPromptToSave = -> true
|
||||
item1.save = jasmine.createSpy("save")
|
||||
item1.saveAs = jasmine.createSpy("saveAs")
|
||||
item1.getUri = -> itemUri
|
||||
|
||||
describe "if the [Save] option is selected", ->
|
||||
describe "when the item has a uri", ->
|
||||
it "saves the item before destroying it", ->
|
||||
itemUri = "test"
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
|
||||
itemUri = null
|
||||
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(item1.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
it "removes and destroys the item without saving it", ->
|
||||
spyOn(atom, 'confirm').andReturn(2)
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
it "does not save, remove, or destroy the item", ->
|
||||
spyOn(atom, 'confirm').andReturn(1)
|
||||
pane.destroyItem(item1)
|
||||
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(item1 in pane.items).toBe true
|
||||
expect(item1.isDestroyed()).toBe false
|
||||
|
||||
describe "when the last item is destroyed", ->
|
||||
it "destroys the pane", ->
|
||||
pane.destroyItem(item) for item in pane.getItems()
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
|
||||
describe "::destroyItems()", ->
|
||||
it "destroys all items and the pane", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.destroyItems()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
expect(item3.isDestroyed()).toBe true
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
expect(pane.items).toEqual []
|
||||
|
||||
describe "when an item emits a destroyed event", ->
|
||||
it "removes it from the list of items", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.items[1].destroy()
|
||||
expect(pane.items).toEqual [item1, item3]
|
||||
|
||||
describe "::destroyInactiveItems()", ->
|
||||
it "destroys all items but the active item", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
[item1, item2, item3] = pane.items
|
||||
pane.activateItem(item2)
|
||||
pane.destroyInactiveItems()
|
||||
expect(pane.items).toEqual [item2]
|
||||
|
||||
describe "::saveActiveItem()", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A")])
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
|
||||
|
||||
describe "when the active item has a uri", ->
|
||||
beforeEach ->
|
||||
pane.activeItem.uri = "test"
|
||||
|
||||
describe "when the active item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
pane.activeItem.save = jasmine.createSpy("save")
|
||||
pane.saveActiveItem()
|
||||
expect(pane.activeItem.save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.save).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
|
||||
describe "when the current item has no uri", ->
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "::saveActiveItemAs()", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A")])
|
||||
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", ->
|
||||
pane.activeItem.path = __filename
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__dirname)
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "::itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3] = pane.items
|
||||
item1.uri = "a"
|
||||
item2.uri = "b"
|
||||
expect(pane.itemForUri("a")).toBe item1
|
||||
expect(pane.itemForUri("b")).toBe item2
|
||||
expect(pane.itemForUri("bogus")).toBeUndefined()
|
||||
|
||||
describe "::moveItem(item, index)", ->
|
||||
it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", ->
|
||||
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
|
||||
[item1, item2, item3, item4] = pane.items
|
||||
pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
|
||||
pane.moveItem(item1, 2)
|
||||
expect(pane.getItems()).toEqual [item2, item3, item1, item4]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 3)
|
||||
expect(pane.getItems()).toEqual [item3, item1, item4, item2]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3)
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(item2, 1)
|
||||
expect(pane.getItems()).toEqual [item3, item2, item1, item4]
|
||||
expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1)
|
||||
|
||||
describe "::moveItemToPane(item, pane, index)", ->
|
||||
[container, pane1, pane2] = []
|
||||
[item1, item2, item3, item4, item5] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
|
||||
container = new PaneContainer(root: pane1)
|
||||
pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")])
|
||||
[item1, item2, item3] = pane1.items
|
||||
[item4, item5] = pane2.items
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
expect(pane1.items).toEqual [item1, item3]
|
||||
expect(pane2.items).toEqual [item4, item2, item5]
|
||||
|
||||
describe "when the moved item the last item in the source pane", ->
|
||||
it "destroys the pane, but not the item", ->
|
||||
item5.destroy()
|
||||
pane2.moveItemToPane(item4, pane1, 0)
|
||||
expect(pane2.isDestroyed()).toBe true
|
||||
expect(item4.isDestroyed()).toBe false
|
||||
|
||||
describe "split methods", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: ["A"])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
describe "::splitLeft(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the left of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitLeft(items: ["B"])
|
||||
pane3 = pane1.splitLeft(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "::splitRight(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'horizontal'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when the parent is a column", ->
|
||||
it "replaces itself with a row and inserts a new pane to the right of itself", ->
|
||||
pane1.splitDown()
|
||||
pane2 = pane1.splitRight(items: ["B"])
|
||||
pane3 = pane1.splitRight(items: ["C"])
|
||||
row = container.root.children[0]
|
||||
expect(row.orientation).toBe 'horizontal'
|
||||
expect(row.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "::splitUp(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane above itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitUp(items: ["B"])
|
||||
pane3 = pane1.splitUp(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane2, pane3, pane1]
|
||||
|
||||
describe "::splitDown(params)", ->
|
||||
describe "when the parent is the container root", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
expect(container.root.orientation).toBe 'vertical'
|
||||
expect(container.root.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
describe "when the parent is a row", ->
|
||||
it "replaces itself with a column and inserts a new pane below itself", ->
|
||||
pane1.splitRight()
|
||||
pane2 = pane1.splitDown(items: ["B"])
|
||||
pane3 = pane1.splitDown(items: ["C"])
|
||||
column = container.root.children[0]
|
||||
expect(column.orientation).toBe 'vertical'
|
||||
expect(column.children).toEqual [pane1, pane3, pane2]
|
||||
|
||||
it "sets up the new pane to be focused", ->
|
||||
expect(pane1.focused).toBe false
|
||||
pane2 = pane1.splitRight()
|
||||
expect(pane2.focused).toBe true
|
||||
|
||||
describe "::destroy()", ->
|
||||
[pane1, container] = []
|
||||
|
||||
beforeEach ->
|
||||
pane1 = new Pane(items: [new Model, new Model])
|
||||
container = new PaneContainer(root: pane1)
|
||||
|
||||
it "destroys the pane's destroyable items", ->
|
||||
[item1, item2] = pane1.items
|
||||
pane1.destroy()
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
expect(item2.isDestroyed()).toBe true
|
||||
|
||||
describe "if the pane is active", ->
|
||||
it "makes the next pane active", ->
|
||||
pane2 = pane1.splitRight()
|
||||
expect(pane2.isActive()).toBe true
|
||||
pane2.destroy()
|
||||
expect(pane1.isActive()).to
|
||||
|
||||
describe "if the pane's parent has more than two children", ->
|
||||
it "removes the pane from its parent", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
|
||||
expect(container.root.children).toEqual [pane1, pane2, pane3]
|
||||
pane2.destroy()
|
||||
expect(container.root.children).toEqual [pane1, pane3]
|
||||
|
||||
describe "if the pane's parent has two children", ->
|
||||
it "replaces the parent with its last remaining child", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
|
||||
expect(container.root.children[0]).toBe pane1
|
||||
expect(container.root.children[1].children).toEqual [pane2, pane3]
|
||||
pane3.destroy()
|
||||
expect(container.root.children).toEqual [pane1, pane2]
|
||||
pane2.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
|
||||
describe "serialization", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane(items: [new Item("A", "a"), new Item("B", "b"), new Item("C", "c")])
|
||||
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.items).toEqual pane.items
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItemAtIndex(1)
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual newPane.items[1]
|
||||
|
||||
it "does not include items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
unserializable = {}
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
it "includes the pane's focus state in the serialized state", ->
|
||||
pane.focus()
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.focused).toBe true
|
@ -5,7 +5,7 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "PaneView", ->
|
||||
[container, view1, view2, editor1, editor2, pane] = []
|
||||
[container, view1, view2, editor1, editor2, pane, paneModel] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@ -23,377 +23,120 @@ describe "PaneView", ->
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = new PaneView(view1, editor1, view2, editor2)
|
||||
paneModel = pane.model
|
||||
container.setRoot(pane)
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe "::initialize(items...)", ->
|
||||
it "displays the first item in the pane", ->
|
||||
expect(pane.itemViews.find('#view-1')).toExist()
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the one being shown and sets the activeItem", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
expect(view1.css('display')).toBe 'none'
|
||||
expect(view2.css('display')).not.toBe 'none'
|
||||
expect(pane.activeItem).toBe view2
|
||||
|
||||
it "triggers 'pane:active-item-changed' if the item isn't already the activeItem", ->
|
||||
pane.activate()
|
||||
it "triggers 'pane:active-item-changed'", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.activeItem).toBe view1
|
||||
pane.activateItem(view2)
|
||||
pane.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
expect(itemChangedHandler.callCount).toBe 1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
pane.activateItem(editor1)
|
||||
paneModel.activateItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
describe "if the pane's active view is focused before calling activateItem", ->
|
||||
it "focuses the new active view", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
expect(pane.activeView).not.toBe view2
|
||||
expect(pane.activeView).toMatchSelector ':focus'
|
||||
pane.activateItem(view2)
|
||||
expect(view2).toMatchSelector ':focus'
|
||||
it "transfers focus to the new active view if the previous view was focused", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
expect(pane.activeView).not.toBe view2
|
||||
expect(pane.activeView).toMatchSelector ':focus'
|
||||
paneModel.activateItem(view2)
|
||||
expect(view2).toMatchSelector ':focus'
|
||||
|
||||
describe "when the given item isn't yet in the items list on the pane", ->
|
||||
view3 = null
|
||||
beforeEach ->
|
||||
view3 = new TestView(id: 'view-3', text: "View 3")
|
||||
pane.activateItem(editor1)
|
||||
expect(pane.getActiveItemIndex()).toBe 1
|
||||
describe "when the new activeItem is a model", ->
|
||||
it "shows the item's view or creates and shows a new view for the item if none exists", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
|
||||
it "adds it to the items list after the active item", ->
|
||||
pane.activateItem(view3)
|
||||
expect(pane.getItems()).toEqual [view1, editor1, view3, view2, editor2]
|
||||
expect(pane.activeItem).toBe view3
|
||||
expect(pane.getActiveItemIndex()).toBe 2
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
it "triggers the 'item-added' event with the item and its index before the 'active-item-changed' event", ->
|
||||
events = []
|
||||
container.on 'pane:item-added', (e, item, index) -> events.push(['pane:item-added', item, index])
|
||||
container.on 'pane:active-item-changed', (e, item) -> events.push(['pane:active-item-changed', item])
|
||||
pane.activateItem(view3)
|
||||
expect(events).toEqual [['pane:item-added', view3, 2], ['pane:active-item-changed', view3]]
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
describe "when showing a model item", ->
|
||||
describe "when no view has yet been appended for that item", ->
|
||||
it "appends and shows a view to display the item based on its `.getViewClass` method", ->
|
||||
pane.activateItem(editor1)
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.editor).toBe editor1
|
||||
paneModel.activateItem(model1)
|
||||
paneModel.activateItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
describe "when a valid view has already been appended for another item", ->
|
||||
it "multiple views are created for multiple items", ->
|
||||
pane.activateItem(editor1)
|
||||
pane.activateItem(editor2)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 2
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.editor).toBe editor2
|
||||
paneModel.activatePreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
it "creates a new view with the item", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
paneModel.destroyItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
paneModel.destroyItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
pane.activateItem(model1)
|
||||
pane.activateItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
pane.activatePreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
pane.destroyItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
pane.destroyItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
describe "when showing a view item", ->
|
||||
describe "when the new activeItem is a view", ->
|
||||
it "appends it to the itemViews div if it hasn't already been appended and shows it", ->
|
||||
expect(pane.itemViews.find('#view-2')).not.toExist()
|
||||
pane.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2')).toExist()
|
||||
expect(pane.activeView).toBe view2
|
||||
paneModel.activateItem(view1)
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2').length).toBe 1
|
||||
|
||||
describe "::destroyItem(item)", ->
|
||||
describe "if the item is not modified", ->
|
||||
it "removes the item and tries to call destroy on it", ->
|
||||
pane.destroyItem(editor2)
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
|
||||
describe "if the item is modified", ->
|
||||
beforeEach ->
|
||||
jasmine.unspy(editor2, 'shouldPromptToSave')
|
||||
spyOn(editor2, 'save')
|
||||
spyOn(editor2, 'saveAs')
|
||||
|
||||
editor2.insertText('a')
|
||||
expect(editor2.isModified()).toBeTruthy()
|
||||
|
||||
describe "if the [Save] option is selected", ->
|
||||
describe "when the item has a uri", ->
|
||||
it "saves the item before removing and destroying it", ->
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
|
||||
editor2.buffer.setPath(undefined)
|
||||
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
it "removes and destroys the item without saving it", ->
|
||||
spyOn(atom, 'confirm').andReturn(2)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
it "does not save, remove, or destroy the item", ->
|
||||
spyOn(atom, 'confirm').andReturn(1)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).not.toBe -1
|
||||
expect(editor2.isDestroyed()).toBe false
|
||||
|
||||
it "removes the item's associated view", ->
|
||||
view1.remove = (selector, keepData) -> @wasRemoved = not keepData
|
||||
pane.destroyItem(view1)
|
||||
expect(view1.wasRemoved).toBe true
|
||||
|
||||
it "removes the item from the items list and shows the next item if it was showing", ->
|
||||
pane.destroyItem(view1)
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
pane.activateItem(editor2)
|
||||
pane.destroyItem(editor2)
|
||||
expect(pane.getItems()).toEqual [editor1, view2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
it "triggers 'pane:item-removed' with the item and its former index", ->
|
||||
describe "when an item is destroyed", ->
|
||||
it "triggers the 'pane:item-removed' event with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
pane.destroyItem(editor1)
|
||||
paneModel.destroyItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when removing the last item", ->
|
||||
it "removes the pane", ->
|
||||
pane.destroyItem(item) for item in pane.getItems()
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
it "shifts focus to the next pane", ->
|
||||
expect(container.getRoot()).toBe pane
|
||||
container.attachToDom()
|
||||
pane2 = pane.splitRight(new TestView(id: 'view-3', text: 'View 3'))
|
||||
pane.focus()
|
||||
expect(pane).toMatchSelector(':has(:focus)')
|
||||
pane.destroyItem(item) for item in pane.getItems()
|
||||
expect(pane2).toMatchSelector ':has(:focus)'
|
||||
|
||||
describe "when the item is a view", ->
|
||||
describe "when the destroyed item is a view", ->
|
||||
it "removes the item from the 'item-views' div", ->
|
||||
expect(view1.parent()).toMatchSelector pane.itemViews
|
||||
pane.destroyItem(view1)
|
||||
paneModel.destroyItem(view1)
|
||||
expect(view1.parent()).not.toMatchSelector pane.itemViews
|
||||
|
||||
describe "when the item is a model", ->
|
||||
it "removes the associated view only when all items that require it have been removed", ->
|
||||
pane.activateItem(editor1)
|
||||
pane.activateItem(editor2)
|
||||
pane.destroyItem(editor2)
|
||||
expect(pane.itemViews.find('.editor')).toExist()
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the associated view", ->
|
||||
paneModel.activateItem(editor1)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 1
|
||||
pane.destroyItem(editor1)
|
||||
expect(pane.itemViews.find('.editor')).not.toExist()
|
||||
expect(pane.itemViews.find('.editor').length).toBe 0
|
||||
|
||||
describe "::moveItem(item, index)", ->
|
||||
it "moves the item to the given index and emits a 'pane:item-moved' event with the item and the new index", ->
|
||||
itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
pane.on 'pane:item-moved', itemMovedHandler
|
||||
|
||||
pane.moveItem(view1, 2)
|
||||
expect(pane.getItems()).toEqual [editor1, view2, view1, editor2]
|
||||
describe "when an item is moved within the same pane", ->
|
||||
it "emits a 'pane:item-moved' event with the item and the new index", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
paneModel.moveItem(view1, 2)
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editor1, 3)
|
||||
expect(pane.getItems()).toEqual [view2, view1, editor2, editor1]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 3]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editor1, 1)
|
||||
expect(pane.getItems()).toEqual [view2, editor1, view1, editor2]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
describe "::moveItemToPane(item, pane, index)", ->
|
||||
[pane2, view3] = []
|
||||
|
||||
beforeEach ->
|
||||
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", ->
|
||||
pane.moveItemToPane(view1, pane2, 1)
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane2.getItems()).toEqual [view3, view1]
|
||||
|
||||
describe "when it is the last item on the source pane", ->
|
||||
it "removes the source pane, but does not destroy the item", ->
|
||||
pane.destroyItem(view1)
|
||||
pane.destroyItem(view2)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
pane.moveItemToPane(editor1, pane2, 1)
|
||||
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(pane2.getItems()).toEqual [view3, editor1]
|
||||
expect(editor1.isDestroyed()).toBe false
|
||||
|
||||
describe "when the item is a jQuery object", ->
|
||||
it "preserves data by detaching instead of removing", ->
|
||||
view1.data('preservative', 1234)
|
||||
pane.moveItemToPane(view1, pane2, 1)
|
||||
pane2.activateItemAtIndex(1)
|
||||
expect(pane2.activeView.data('preservative')).toBe 1234
|
||||
|
||||
describe "pane:close", ->
|
||||
it "destroys all items and removes the pane", ->
|
||||
pane.activateItem(editor1)
|
||||
pane.trigger 'pane:close'
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
expect(editor1.isDestroyed()).toBe true
|
||||
|
||||
describe "pane:close-other-items", ->
|
||||
it "destroys all items except the current", ->
|
||||
pane.activateItem(editor1)
|
||||
pane.trigger 'pane:close-other-items'
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
|
||||
describe "::saveActiveItem()", ->
|
||||
describe "when the current item has a uri", ->
|
||||
describe "when the current item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
spyOn(editor2, 'save')
|
||||
pane.activateItem(editor2)
|
||||
pane.saveActiveItem()
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
pane.activeItem.getUri = -> 'you are eye'
|
||||
expect(pane.activeItem.save).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
|
||||
describe "when the current item has no uri", ->
|
||||
beforeEach ->
|
||||
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", ->
|
||||
newEditor = atom.project.openSync()
|
||||
spyOn(newEditor, 'saveAs')
|
||||
pane.activateItem(newEditor)
|
||||
|
||||
pane.saveActiveItem()
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(newEditor.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItem()
|
||||
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
|
||||
|
||||
describe "::saveActiveItemAs()", ->
|
||||
beforeEach ->
|
||||
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", ->
|
||||
spyOn(editor2, 'saveAs')
|
||||
pane.activateItem(editor2)
|
||||
|
||||
pane.saveActiveItemAs()
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editor2.getPath()))
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
expect(pane.activeItem.saveAs).toBeUndefined()
|
||||
pane.saveActiveItemAs()
|
||||
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", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe view2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe view1
|
||||
|
||||
describe "pane:show-item-N events", ->
|
||||
it "shows the (n-1)th item if it exists", ->
|
||||
pane.trigger 'pane:show-item-2'
|
||||
expect(pane.activeItem).toBe pane.itemAtIndex(1)
|
||||
pane.trigger 'pane:show-item-1'
|
||||
expect(pane.activeItem).toBe pane.itemAtIndex(0)
|
||||
pane.trigger 'pane:show-item-9' # don't fail on out-of-bounds indices
|
||||
expect(pane.activeItem).toBe pane.itemAtIndex(0)
|
||||
describe "when an item is moved to another pane", ->
|
||||
it "detaches the item's view rather than removing it", ->
|
||||
paneModel2 = paneModel.splitRight()
|
||||
view1.data('preservative', 1234)
|
||||
paneModel.moveItemToPane(view1, paneModel2, 1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
paneModel2.activateItemAtIndex(1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
@ -424,12 +167,7 @@ describe "PaneView", ->
|
||||
waitsFor ->
|
||||
pane.items.length == 4
|
||||
|
||||
describe "::remove()", ->
|
||||
it "destroys all the pane's items", ->
|
||||
pane.remove()
|
||||
expect(editor1.isDestroyed()).toBe true
|
||||
expect(editor2.isDestroyed()).toBe true
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
container.on 'pane:removed', removedHandler
|
||||
@ -437,52 +175,28 @@ describe "PaneView", ->
|
||||
expect(removedHandler).toHaveBeenCalled()
|
||||
expect(removedHandler.argsForCall[0][1]).toBe pane
|
||||
|
||||
describe "when there are other panes", ->
|
||||
describe "if the destroyed pane has focus", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
beforeEach ->
|
||||
pane.activateItem(editor1)
|
||||
paneToLeft = pane.splitLeft(pane.copyActiveItem())
|
||||
paneToRight = pane.splitRight(pane.copyActiveItem())
|
||||
container.attachToDom()
|
||||
describe "if it is not the last pane in the container", ->
|
||||
it "focuses the next pane", ->
|
||||
paneModel.activateItem(editor1)
|
||||
pane2Model = paneModel.splitRight(items: [paneModel.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
container.attachToDom()
|
||||
expect(pane.hasFocus()).toBe false
|
||||
pane2Model.destroy()
|
||||
expect(pane.hasFocus()).toBe true
|
||||
|
||||
describe "when the removed pane is active", ->
|
||||
it "makes the next the next pane active and focuses it", ->
|
||||
pane.activate()
|
||||
pane.remove()
|
||||
expect(paneToLeft.isActive()).toBeFalsy()
|
||||
expect(paneToRight.isActive()).toBeTruthy()
|
||||
expect(paneToRight).toMatchSelector ':has(:focus)'
|
||||
|
||||
describe "when the removed pane is not active", ->
|
||||
it "does not affect the active pane or the focus", ->
|
||||
paneToLeft.focus()
|
||||
expect(paneToLeft.isActive()).toBeTruthy()
|
||||
expect(paneToRight.isActive()).toBeFalsy()
|
||||
|
||||
pane.remove()
|
||||
expect(paneToLeft.isActive()).toBeTruthy()
|
||||
expect(paneToRight.isActive()).toBeFalsy()
|
||||
expect(paneToLeft).toMatchSelector ':has(:focus)'
|
||||
|
||||
describe "when it is the last pane", ->
|
||||
beforeEach ->
|
||||
expect(container.getPanes().length).toBe 1
|
||||
atom.workspaceView = focus: jasmine.createSpy("workspaceView.focus")
|
||||
|
||||
describe "when the removed pane is focused", ->
|
||||
it "calls focus on workspaceView so we don't lose focus", ->
|
||||
describe "if it is the last pane in the container", ->
|
||||
it "shifts focus to the workspace view", ->
|
||||
atom.workspaceView = {focus: jasmine.createSpy("atom.workspaceView.focus")}
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
pane.remove()
|
||||
expect(container.hasFocus()).toBe true
|
||||
paneModel.destroy()
|
||||
expect(atom.workspaceView.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the removed pane is not focused", ->
|
||||
it "does not call focus on root view", ->
|
||||
expect(pane).not.toMatchSelector ':has(:focus)'
|
||||
pane.remove()
|
||||
expect(atom.workspaceView.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "::getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.activateItem(editor1)
|
||||
@ -491,148 +205,78 @@ describe "PaneView", ->
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
expect(pane2Model.isActive()).toBe true
|
||||
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
expect(pane).not.toHaveClass('active')
|
||||
paneModel.activate()
|
||||
expect(pane).toHaveClass('active')
|
||||
pane2Model.activate()
|
||||
expect(pane).not.toHaveClass('active')
|
||||
|
||||
it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", ->
|
||||
pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler")
|
||||
pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
paneModel.activate()
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 0
|
||||
|
||||
pane2Model.activate()
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
beforeEach ->
|
||||
container.attachToDom()
|
||||
|
||||
it "focuses the active item view", ->
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.activeItem.on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
it "triggers 'pane:became-active' if it was not previously active", ->
|
||||
pane2 = pane.splitRight(view2) # Make pane inactive
|
||||
it "makes the pane active", ->
|
||||
paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
expect(paneModel.isActive()).toBe false
|
||||
pane.focus()
|
||||
expect(paneModel.isActive()).toBe true
|
||||
|
||||
becameActiveHandler = jasmine.createSpy("becameActiveHandler")
|
||||
pane.on 'pane:became-active', becameActiveHandler
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
pane.focusin()
|
||||
expect(pane.isActive()).toBeTruthy()
|
||||
pane.focusin()
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
|
||||
it "triggers 'pane:became-inactive' when it was previously active", ->
|
||||
pane2 = pane.splitRight(view2) # Make pane inactive
|
||||
|
||||
becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
pane.on 'pane:became-inactive', becameInactiveHandler
|
||||
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
pane.focusin()
|
||||
expect(pane.isActive()).toBeTruthy()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "split methods", ->
|
||||
[pane1, view3, view4] = []
|
||||
beforeEach ->
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriate pane-row and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.model
|
||||
pane.activateItem(editor1)
|
||||
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", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane1.splitRight(pane1.copyActiveItem())
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
pane3 = pane3Model._view
|
||||
|
||||
pane3 = pane2.splitRight(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]]
|
||||
expect(container.find('> .pane-row > .pane').toArray()).toEqual [pane1[0]]
|
||||
expect(container.find('> .pane-row > .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
it "builds a row if needed, then appends a new pane after itself ", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane1.splitRight()
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
expect(pane2.items).toEqual []
|
||||
expect(pane2.activeItem).toBeUndefined()
|
||||
|
||||
pane3 = pane2.splitRight()
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0], pane3[0]]
|
||||
expect(pane3.items).toEqual []
|
||||
expect(pane3.activeItem).toBeUndefined()
|
||||
|
||||
describe "splitLeft(items...)", ->
|
||||
it "builds a row if needed, then appends a new pane before itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitLeft(pane1.copyActiveItem())
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitLeft(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
expect(container.find('.pane-row .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]]
|
||||
|
||||
describe "splitDown(items...)", ->
|
||||
it "builds a column if needed, then appends a new pane after itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitDown(pane1.copyActiveItem())
|
||||
expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitDown(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]]
|
||||
|
||||
describe "splitUp(items...)", ->
|
||||
it "builds a column if needed, then appends a new pane before itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitUp(pane1.copyActiveItem())
|
||||
expect(container.find('.pane-column .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitUp(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
expect(container.find('.pane-column .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]]
|
||||
|
||||
describe "::itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
expect(pane.itemForUri(editor1.getUri())).toBe editor1
|
||||
expect(pane.itemForUri(editor2.getUri())).toBe editor2
|
||||
pane1Model.destroy()
|
||||
expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
describe "serialization", ->
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2]
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItem(editor2)
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual editor2
|
||||
|
||||
it "does not show items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
|
||||
class Unserializable
|
||||
getViewClass: -> TestView
|
||||
|
||||
pane.activateItem(new Unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
|
||||
container2 = container.testSerialization()
|
||||
container2 = new PaneContainerView(container.model.testSerialization())
|
||||
pane2 = container2.getRoot()
|
||||
container2.attachToDom()
|
||||
expect(pane2).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
container3 = container.testSerialization()
|
||||
container3 = new PaneContainerView(container.model.testSerialization())
|
||||
pane3 = container3.getRoot()
|
||||
container3.attachToDom()
|
||||
expect(pane3).not.toMatchSelector(':has(:focus)')
|
||||
|
@ -33,7 +33,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@ -43,13 +43,13 @@ describe "SelectList", ->
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays an error if there are no matches, removes error when there are matches", ->
|
||||
miniEditor.insertText('nothing will match this')
|
||||
miniEditor.getEditor().insertText('nothing will match this')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).not.toBeHidden()
|
||||
|
||||
miniEditor.setText('la')
|
||||
miniEditor.getEditor().setText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@ -58,7 +58,7 @@ describe "SelectList", ->
|
||||
it "displays no elements until the array has been set on the list", ->
|
||||
selectList.array = null
|
||||
selectList.list.empty()
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
@ -124,7 +124,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "does not trigger the confirmed hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
@ -132,7 +132,7 @@ describe "SelectList", ->
|
||||
expect(selectList.confirmed).not.toHaveBeenCalled()
|
||||
|
||||
it "does trigger the cancelled hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
|
@ -107,7 +107,7 @@ afterEach ->
|
||||
|
||||
atom.workspaceView?.remove?()
|
||||
atom.workspaceView = null
|
||||
delete atom.state.workspaceView
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy?()
|
||||
atom.project = null
|
||||
|
@ -88,12 +88,12 @@ describe "Window", ->
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceViewState = atom.workspaceView.serialize()
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.state.workspaceView).toEqual workspaceViewState
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.syntax).toEqual syntaxState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
|
@ -3,6 +3,7 @@ Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneView = require '../src/pane-view'
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
describe "WorkspaceView", ->
|
||||
pathToOpen = null
|
||||
@ -10,7 +11,8 @@ describe "WorkspaceView", ->
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
@ -19,20 +21,21 @@ describe "WorkspaceView", ->
|
||||
viewState = null
|
||||
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspaceView.serialize()
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
atom.workspaceView.remove()
|
||||
atom.project = atom.deserializers.deserialize(projectState)
|
||||
atom.workspaceView = WorkspaceView.deserialize(workspaceState)
|
||||
atom.workspace = Workspace.deserialize(workspaceState)
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
atom.workspaceView.openSync()
|
||||
editor1 = atom.workspaceView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
|
||||
simulateReload()
|
||||
@ -58,31 +61,31 @@ describe "WorkspaceView", ->
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editor1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editor3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editor2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editor4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe atom.project.resolve('a')
|
||||
expect(editor2.getPath()).toBe atom.project.resolve('b')
|
||||
expect(editor3.getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().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
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.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 editorView is focused again
|
||||
expect(editorView2.isFocused).toBeTruthy()
|
||||
expect(editorView1.isFocused).toBeFalsy()
|
||||
expect(editorView3.isFocused).toBeFalsy()
|
||||
expect(editorView4.isFocused).toBeFalsy()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}"
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
@ -187,7 +190,7 @@ describe "WorkspaceView", ->
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
workspaceView2 = atom.deserializers.deserialize(atom.workspaceView.serialize())
|
||||
workspaceView2 = new WorkspaceView(atom.workspace.testSerialization())
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
workspaceView2.remove()
|
||||
@ -462,27 +465,27 @@ describe "WorkspaceView", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditor = atom.workspaceView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText(" \t ")
|
||||
leftEditorView = rightEditorView.splitLeft()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
withInvisiblesShowing = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
lowerLeftEditorView = leftEditorView.splitDown()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditor = rightEditor.splitDown()
|
||||
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
||||
lowerRightEditorView = rightEditorView.splitDown()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
beforeEach ->
|
||||
|
@ -232,8 +232,10 @@ class Atom extends Model
|
||||
|
||||
# Private:
|
||||
deserializeWorkspaceView: ->
|
||||
Workspace = require './workspace'
|
||||
WorkspaceView = require './workspace-view'
|
||||
@workspaceView = @deserializers.deserialize(@state.workspaceView) ? new WorkspaceView
|
||||
@workspace = Workspace.deserialize(@state.workspace) ? new Workspace
|
||||
@workspaceView = new WorkspaceView(@workspace)
|
||||
$(@workspaceViewParentSelector).append(@workspaceView)
|
||||
|
||||
# Private:
|
||||
@ -279,7 +281,7 @@ class Atom extends Model
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.workspaceView = @workspaceView.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@saveSync()
|
||||
|
@ -49,7 +49,7 @@ class CursorView extends View
|
||||
else if !@startBlinkingTimeout
|
||||
@startBlinking()
|
||||
|
||||
@setVisible(@cursor.isVisible() and not @editorView.isFoldedAtScreenRow(screenPosition.row))
|
||||
@setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row))
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
|
@ -119,393 +119,118 @@ class EditorView extends View
|
||||
# Some commands are excluded from mini-editors.
|
||||
bindKeys: ->
|
||||
editorBindings =
|
||||
'core:move-left': @moveCursorLeft
|
||||
'core:move-right': @moveCursorRight
|
||||
'core:select-left': @selectLeft
|
||||
'core:select-right': @selectRight
|
||||
'core:select-all': @selectAll
|
||||
'core:backspace': @backspace
|
||||
'core:delete': @delete
|
||||
'core:undo': @undo
|
||||
'core:redo': @redo
|
||||
'core:cut': @cutSelection
|
||||
'core:copy': @copySelection
|
||||
'core:paste': @paste
|
||||
'editor:move-to-previous-word': @moveCursorToPreviousWord
|
||||
'editor:select-word': @selectWord
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:backspace-to-beginning-of-word': @backspaceToBeginningOfWord
|
||||
'editor:backspace-to-beginning-of-line': @backspaceToBeginningOfLine
|
||||
'editor:delete-to-end-of-word': @deleteToEndOfWord
|
||||
'editor:delete-line': @deleteLine
|
||||
'editor:cut-to-end-of-line': @cutToEndOfLine
|
||||
'core:move-left': => @editor.moveCursorLeft()
|
||||
'core:move-right': => @editor.moveCursorRight()
|
||||
'core:select-left': => @editor.selectLeft()
|
||||
'core:select-right': => @editor.selectRight()
|
||||
'core:select-all': => @editor.selectAll()
|
||||
'core:backspace': => @editor.backspace()
|
||||
'core:delete': => @editor.delete()
|
||||
'core:undo': => @editor.undo()
|
||||
'core:redo': => @editor.redo()
|
||||
'core:cut': => @editor.cutSelectedText()
|
||||
'core:copy': => @editor.copySelectedText()
|
||||
'core:paste': => @editor.pasteText()
|
||||
'editor:move-to-previous-word': => @editor.moveCursorToPreviousWord()
|
||||
'editor:select-word': => @editor.selectWord()
|
||||
'editor:consolidate-selections': (event) => @consolidateSelections(event)
|
||||
'editor:backspace-to-beginning-of-word': => @editor.backspaceToBeginningOfWord()
|
||||
'editor:backspace-to-beginning-of-line': => @editor.backspaceToBeginningOfLine()
|
||||
'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => @editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => @editor.cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-screen-line': => @editor.moveCursorToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': @moveCursorToBeginningOfLine
|
||||
'editor:move-to-beginning-of-line': => @editor.moveCursorToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': => @editor.moveCursorToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': @moveCursorToEndOfLine
|
||||
'editor:move-to-first-character-of-line': @moveCursorToFirstCharacterOfLine
|
||||
'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord
|
||||
'editor:move-to-end-of-word': @moveCursorToEndOfWord
|
||||
'editor:move-to-beginning-of-next-word': @moveCursorToBeginningOfNextWord
|
||||
'editor:move-to-previous-word-boundary': @moveCursorToPreviousWordBoundary
|
||||
'editor:move-to-next-word-boundary': @moveCursorToNextWordBoundary
|
||||
'editor:select-to-end-of-line': @selectToEndOfLine
|
||||
'editor:select-to-beginning-of-line': @selectToBeginningOfLine
|
||||
'editor:select-to-end-of-word': @selectToEndOfWord
|
||||
'editor:select-to-beginning-of-word': @selectToBeginningOfWord
|
||||
'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord
|
||||
'editor:select-to-next-word-boundary': @selectToNextWordBoundary
|
||||
'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary
|
||||
'editor:select-to-first-character-of-line': @selectToFirstCharacterOfLine
|
||||
'editor:select-line': @selectLine
|
||||
'editor:transpose': @transpose
|
||||
'editor:upper-case': @upperCase
|
||||
'editor:lower-case': @lowerCase
|
||||
'editor:move-to-end-of-line': => @editor.moveCursorToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': => @editor.moveCursorToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': => @editor.moveCursorToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': => @editor.moveCursorToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': => @editor.moveCursorToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': => @editor.moveCursorToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': => @editor.moveCursorToNextWordBoundary()
|
||||
'editor:select-to-end-of-line': => @editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': => @editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': => @editor.selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': => @editor.selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': => @editor.selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': => @editor.selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': => @editor.selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': => @editor.selectToFirstCharacterOfLine()
|
||||
'editor:select-line': => @editor.selectLine()
|
||||
'editor:transpose': => @editor.transpose()
|
||||
'editor:upper-case': => @editor.upperCase()
|
||||
'editor:lower-case': => @editor.lowerCase()
|
||||
|
||||
unless @mini
|
||||
_.extend editorBindings,
|
||||
'core:move-up': @moveCursorUp
|
||||
'core:move-down': @moveCursorDown
|
||||
'core:move-to-top': @moveCursorToTop
|
||||
'core:move-to-bottom': @moveCursorToBottom
|
||||
'core:page-down': @pageDown
|
||||
'core:page-up': @pageUp
|
||||
'core:select-up': @selectUp
|
||||
'core:select-down': @selectDown
|
||||
'core:select-to-top': @selectToTop
|
||||
'core:select-to-bottom': @selectToBottom
|
||||
'editor:indent': @indent
|
||||
'editor:auto-indent': @autoIndent
|
||||
'editor:indent-selected-rows': @indentSelectedRows
|
||||
'editor:outdent-selected-rows': @outdentSelectedRows
|
||||
'editor:newline': @insertNewline
|
||||
'editor:newline-below': @insertNewlineBelow
|
||||
'editor:newline-above': @insertNewlineAbove
|
||||
'editor:add-selection-below': @addSelectionBelow
|
||||
'editor:add-selection-above': @addSelectionAbove
|
||||
'core:move-up': => @editor.moveCursorUp()
|
||||
'core:move-down': => @editor.moveCursorDown()
|
||||
'core:move-to-top': => @editor.moveCursorToTop()
|
||||
'core:move-to-bottom': => @editor.moveCursorToBottom()
|
||||
'core:page-down': => @editor.pageDown()
|
||||
'core:page-up': => @editor.pageUp()
|
||||
'core:select-up': => @editor.selectUp()
|
||||
'core:select-down': => @editor.selectDown()
|
||||
'core:select-to-top': => @editor.selectToTop()
|
||||
'core:select-to-bottom': => @editor.selectToBottom()
|
||||
'editor:indent': => @editor.indent()
|
||||
'editor:auto-indent': => @editor.autoIndent()
|
||||
'editor:indent-selected-rows': => @editor.indentSelectedRows()
|
||||
'editor:outdent-selected-rows': => @editor.outdentSelectedRows()
|
||||
'editor:newline': => @editor.insertNewline()
|
||||
'editor:newline-below': => @editor.insertNewlineBelow()
|
||||
'editor:newline-above': => @editor.insertNewlineAbove()
|
||||
'editor:add-selection-below': => @editor.addSelectionBelow()
|
||||
'editor:add-selection-above': => @editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': => @editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': @toggleSoftTabs
|
||||
'editor:toggle-soft-wrap': @toggleSoftWrap
|
||||
'editor:fold-all': @foldAll
|
||||
'editor:unfold-all': @unfoldAll
|
||||
'editor:fold-current-row': @foldCurrentRow
|
||||
'editor:unfold-current-row': @unfoldCurrentRow
|
||||
'editor:fold-selection': @foldSelection
|
||||
'editor:fold-at-indent-level-1': => @foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => @foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => @foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => @foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => @foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => @foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => @foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => @foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => @foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': @toggleLineCommentsInSelection
|
||||
'editor:log-cursor-scope': @logCursorScope
|
||||
'editor:checkout-head-revision': @checkoutHead
|
||||
'editor:copy-path': @copyPathToPasteboard
|
||||
'editor:move-line-up': @moveLineUp
|
||||
'editor:move-line-down': @moveLineDown
|
||||
'editor:duplicate-line': @duplicateLine
|
||||
'editor:join-line': @joinLine
|
||||
'editor:toggle-soft-tabs': => @toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': => @toggleSoftWrap()
|
||||
'editor:fold-all': => @editor.foldAll()
|
||||
'editor:unfold-all': => @editor.unfoldAll()
|
||||
'editor:fold-current-row': => @editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': => @editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': => @editor.foldSelection()
|
||||
'editor:fold-at-indent-level-1': => @editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => @editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => @editor.foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => @editor.foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => @editor.foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => @editor.foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => @editor.foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => @editor.foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => @editor.foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': => @toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': => @logCursorScope()
|
||||
'editor:checkout-head-revision': => @checkoutHead()
|
||||
'editor:copy-path': => @copyPathToPasteboard()
|
||||
'editor:move-line-up': => @editor.moveLineUp()
|
||||
'editor:move-line-down': => @editor.moveLineDown()
|
||||
'editor:duplicate-line': => @editor.duplicateLine()
|
||||
'editor:join-line': => @editor.joinLine()
|
||||
'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide')
|
||||
'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers')
|
||||
'editor:scroll-to-cursor': @scrollToCursorPosition
|
||||
'editor:scroll-to-cursor': => @scrollToCursorPosition()
|
||||
|
||||
documentation = {}
|
||||
for name, method of editorBindings
|
||||
do (name, method) =>
|
||||
@command name, (e) => method.call(this, e); false
|
||||
@command name, (e) -> method(e); false
|
||||
|
||||
# {Delegates to: Editor.getCursor}
|
||||
getCursor: -> @editor.getCursor()
|
||||
getEditor: ->
|
||||
@editor
|
||||
|
||||
# {Delegates to: Editor.getCursors}
|
||||
getCursors: -> @editor.getCursors()
|
||||
# {Delegates to: Editor.getText}
|
||||
getText: ->
|
||||
@editor.getText()
|
||||
|
||||
# {Delegates to: Editor.addCursorAtScreenPosition}
|
||||
addCursorAtScreenPosition: (screenPosition) -> @editor.addCursorAtScreenPosition(screenPosition)
|
||||
|
||||
# {Delegates to: Editor.addCursorAtBufferPosition}
|
||||
addCursorAtBufferPosition: (bufferPosition) -> @editor.addCursorAtBufferPosition(bufferPosition)
|
||||
|
||||
# {Delegates to: Editor.moveCursorUp}
|
||||
moveCursorUp: -> @editor.moveCursorUp()
|
||||
|
||||
# {Delegates to: Editor.moveCursorDown}
|
||||
moveCursorDown: -> @editor.moveCursorDown()
|
||||
|
||||
# {Delegates to: Editor.moveCursorLeft}
|
||||
moveCursorLeft: -> @editor.moveCursorLeft()
|
||||
|
||||
# {Delegates to: Editor.moveCursorRight}
|
||||
moveCursorRight: -> @editor.moveCursorRight()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfWord}
|
||||
moveCursorToBeginningOfWord: -> @editor.moveCursorToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToEndOfWord}
|
||||
moveCursorToEndOfWord: -> @editor.moveCursorToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfNextWord}
|
||||
moveCursorToBeginningOfNextWord: -> @editor.moveCursorToBeginningOfNextWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToTop}
|
||||
moveCursorToTop: -> @editor.moveCursorToTop()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBottom}
|
||||
moveCursorToBottom: -> @editor.moveCursorToBottom()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfLine}
|
||||
moveCursorToBeginningOfLine: -> @editor.moveCursorToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToFirstCharacterOfLine}
|
||||
moveCursorToFirstCharacterOfLine: -> @editor.moveCursorToFirstCharacterOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToPreviousWordBoundary}
|
||||
moveCursorToPreviousWordBoundary: -> @editor.moveCursorToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToNextWordBoundary}
|
||||
moveCursorToNextWordBoundary: -> @editor.moveCursorToNextWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToEndOfLine}
|
||||
moveCursorToEndOfLine: -> @editor.moveCursorToEndOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveLineUp}
|
||||
moveLineUp: -> @editor.moveLineUp()
|
||||
|
||||
# {Delegates to: Editor.moveLineDown}
|
||||
moveLineDown: -> @editor.moveLineDown()
|
||||
|
||||
# {Delegates to: Editor.setCursorScreenPosition}
|
||||
setCursorScreenPosition: (position, options) -> @editor.setCursorScreenPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.duplicateLine}
|
||||
duplicateLine: -> @editor.duplicateLine()
|
||||
|
||||
# {Delegates to: Editor.joinLine}
|
||||
joinLine: -> @editor.joinLine()
|
||||
|
||||
# {Delegates to: Editor.getCursorScreenPosition}
|
||||
getCursorScreenPosition: -> @editor.getCursorScreenPosition()
|
||||
|
||||
# {Delegates to: Editor.getCursorScreenRow}
|
||||
getCursorScreenRow: -> @editor.getCursorScreenRow()
|
||||
|
||||
# {Delegates to: Editor.setCursorBufferPosition}
|
||||
setCursorBufferPosition: (position, options) -> @editor.setCursorBufferPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.getCursorBufferPosition}
|
||||
getCursorBufferPosition: -> @editor.getCursorBufferPosition()
|
||||
|
||||
# {Delegates to: Editor.getCurrentParagraphBufferRange}
|
||||
getCurrentParagraphBufferRange: -> @editor.getCurrentParagraphBufferRange()
|
||||
|
||||
# {Delegates to: Editor.getWordUnderCursor}
|
||||
getWordUnderCursor: (options) -> @editor.getWordUnderCursor(options)
|
||||
|
||||
# {Delegates to: Editor.getSelection}
|
||||
getSelection: (index) -> @editor.getSelection(index)
|
||||
|
||||
# {Delegates to: Editor.getSelections}
|
||||
getSelections: -> @editor.getSelections()
|
||||
|
||||
# {Delegates to: Editor.getSelectionsOrderedByBufferPosition}
|
||||
getSelectionsOrderedByBufferPosition: -> @editor.getSelectionsOrderedByBufferPosition()
|
||||
|
||||
# {Delegates to: Editor.getLastSelectionInBuffer}
|
||||
getLastSelectionInBuffer: -> @editor.getLastSelectionInBuffer()
|
||||
|
||||
# {Delegates to: Editor.getSelectedText}
|
||||
getSelectedText: -> @editor.getSelectedText()
|
||||
|
||||
# {Delegates to: Editor.getSelectedBufferRanges}
|
||||
getSelectedBufferRanges: -> @editor.getSelectedBufferRanges()
|
||||
|
||||
# {Delegates to: Editor.getSelectedBufferRange}
|
||||
getSelectedBufferRange: -> @editor.getSelectedBufferRange()
|
||||
|
||||
# {Delegates to: Editor.setSelectedBufferRange}
|
||||
setSelectedBufferRange: (bufferRange, options) -> @editor.setSelectedBufferRange(bufferRange, options)
|
||||
|
||||
# {Delegates to: Editor.setSelectedBufferRanges}
|
||||
setSelectedBufferRanges: (bufferRanges, options) -> @editor.setSelectedBufferRanges(bufferRanges, options)
|
||||
|
||||
# {Delegates to: Editor.addSelectionForBufferRange}
|
||||
addSelectionForBufferRange: (bufferRange, options) -> @editor.addSelectionForBufferRange(bufferRange, options)
|
||||
|
||||
# {Delegates to: Editor.selectRight}
|
||||
selectRight: -> @editor.selectRight()
|
||||
|
||||
# {Delegates to: Editor.selectLeft}
|
||||
selectLeft: -> @editor.selectLeft()
|
||||
|
||||
# {Delegates to: Editor.selectUp}
|
||||
selectUp: -> @editor.selectUp()
|
||||
|
||||
# {Delegates to: Editor.selectDown}
|
||||
selectDown: -> @editor.selectDown()
|
||||
|
||||
# {Delegates to: Editor.selectToTop}
|
||||
selectToTop: -> @editor.selectToTop()
|
||||
|
||||
# {Delegates to: Editor.selectToBottom}
|
||||
selectToBottom: -> @editor.selectToBottom()
|
||||
|
||||
# {Delegates to: Editor.selectAll}
|
||||
selectAll: -> @editor.selectAll()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfLine}
|
||||
selectToBeginningOfLine: -> @editor.selectToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToFirstCharacterOfLine}
|
||||
selectToFirstCharacterOfLine: -> @editor.selectToFirstCharacterOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToEndOfLine}
|
||||
selectToEndOfLine: -> @editor.selectToEndOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToPreviousWordBoundary}
|
||||
selectToPreviousWordBoundary: -> @editor.selectToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.selectToNextWordBoundary}
|
||||
selectToNextWordBoundary: -> @editor.selectToNextWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.addSelectionBelow}
|
||||
addSelectionBelow: -> @editor.addSelectionBelow()
|
||||
|
||||
# {Delegates to: Editor.addSelectionAbove}
|
||||
addSelectionAbove: -> @editor.addSelectionAbove()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfWord}
|
||||
selectToBeginningOfWord: -> @editor.selectToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.selectToEndOfWord}
|
||||
selectToEndOfWord: -> @editor.selectToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfNextWord}
|
||||
selectToBeginningOfNextWord: -> @editor.selectToBeginningOfNextWord()
|
||||
|
||||
# {Delegates to: Editor.selectWord}
|
||||
selectWord: -> @editor.selectWord()
|
||||
|
||||
# {Delegates to: Editor.selectLine}
|
||||
selectLine: -> @editor.selectLine()
|
||||
|
||||
# {Delegates to: Editor.selectToScreenPosition}
|
||||
selectToScreenPosition: (position) -> @editor.selectToScreenPosition(position)
|
||||
|
||||
# {Delegates to: Editor.transpose}
|
||||
transpose: -> @editor.transpose()
|
||||
|
||||
# {Delegates to: Editor.upperCase}
|
||||
upperCase: -> @editor.upperCase()
|
||||
|
||||
# {Delegates to: Editor.lowerCase}
|
||||
lowerCase: -> @editor.lowerCase()
|
||||
|
||||
# {Delegates to: Editor.clearSelections}
|
||||
clearSelections: -> @editor.clearSelections()
|
||||
|
||||
# {Delegates to: Editor.backspace}
|
||||
backspace: -> @editor.backspace()
|
||||
|
||||
# {Delegates to: Editor.backspaceToBeginningOfWord}
|
||||
backspaceToBeginningOfWord: -> @editor.backspaceToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.backspaceToBeginningOfLine}
|
||||
backspaceToBeginningOfLine: -> @editor.backspaceToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.delete}
|
||||
delete: -> @editor.delete()
|
||||
|
||||
# {Delegates to: Editor.deleteToEndOfWord}
|
||||
deleteToEndOfWord: -> @editor.deleteToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.deleteLine}
|
||||
deleteLine: -> @editor.deleteLine()
|
||||
|
||||
# {Delegates to: Editor.cutToEndOfLine}
|
||||
cutToEndOfLine: -> @editor.cutToEndOfLine()
|
||||
# {Delegates to: Editor.setText}
|
||||
setText: (text) ->
|
||||
@editor.setText(text)
|
||||
|
||||
# {Delegates to: Editor.insertText}
|
||||
insertText: (text, options) -> @editor.insertText(text, options)
|
||||
|
||||
# {Delegates to: Editor.insertNewline}
|
||||
insertNewline: -> @editor.insertNewline()
|
||||
|
||||
# {Delegates to: Editor.insertNewlineBelow}
|
||||
insertNewlineBelow: -> @editor.insertNewlineBelow()
|
||||
|
||||
# {Delegates to: Editor.insertNewlineAbove}
|
||||
insertNewlineAbove: -> @editor.insertNewlineAbove()
|
||||
|
||||
# {Delegates to: Editor.indent}
|
||||
indent: (options) -> @editor.indent(options)
|
||||
|
||||
# {Delegates to: Editor.autoIndentSelectedRows}
|
||||
autoIndent: (options) -> @editor.autoIndentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.indentSelectedRows}
|
||||
indentSelectedRows: -> @editor.indentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.outdentSelectedRows}
|
||||
outdentSelectedRows: -> @editor.outdentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.cutSelectedText}
|
||||
cutSelection: -> @editor.cutSelectedText()
|
||||
|
||||
# {Delegates to: Editor.copySelectedText}
|
||||
copySelection: -> @editor.copySelectedText()
|
||||
|
||||
# {Delegates to: Editor.pasteText}
|
||||
paste: (options) -> @editor.pasteText(options)
|
||||
|
||||
# {Delegates to: Editor.undo}
|
||||
undo: -> @editor.undo()
|
||||
|
||||
# {Delegates to: Editor.redo}
|
||||
redo: -> @editor.redo()
|
||||
|
||||
# {Delegates to: Editor.createFold}
|
||||
createFold: (startRow, endRow) -> @editor.createFold(startRow, endRow)
|
||||
|
||||
# {Delegates to: Editor.foldCurrentRow}
|
||||
foldCurrentRow: -> @editor.foldCurrentRow()
|
||||
|
||||
# {Delegates to: Editor.unfoldCurrentRow}
|
||||
unfoldCurrentRow: -> @editor.unfoldCurrentRow()
|
||||
|
||||
# {Delegates to: Editor.foldAll}
|
||||
foldAll: -> @editor.foldAll()
|
||||
|
||||
# {Delegates to: Editor.unfoldAll}
|
||||
unfoldAll: -> @editor.unfoldAll()
|
||||
|
||||
# {Delegates to: Editor.foldSelection}
|
||||
foldSelection: -> @editor.foldSelection()
|
||||
|
||||
# {Delegates to: Editor.destroyFoldsContainingBufferRow}
|
||||
destroyFoldsContainingBufferRow: (bufferRow) -> @editor.destroyFoldsContainingBufferRow(bufferRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtScreenRow}
|
||||
isFoldedAtScreenRow: (screenRow) -> @editor.isFoldedAtScreenRow(screenRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtBufferRow}
|
||||
isFoldedAtBufferRow: (bufferRow) -> @editor.isFoldedAtBufferRow(bufferRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtCursorRow}
|
||||
isFoldedAtCursorRow: -> @editor.isFoldedAtCursorRow()
|
||||
|
||||
foldAllAtIndentLevel: (indentLevel) -> @editor.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# {Delegates to: Editor.lineForScreenRow}
|
||||
lineForScreenRow: (screenRow) -> @editor.lineForScreenRow(screenRow)
|
||||
|
||||
# {Delegates to: Editor.linesForScreenRows}
|
||||
linesForScreenRows: (start, end) -> @editor.linesForScreenRows(start, end)
|
||||
|
||||
# {Delegates to: Editor.getScreenLineCount}
|
||||
getScreenLineCount: -> @editor.getScreenLineCount()
|
||||
insertText: (text, options) ->
|
||||
@editor.insertText(text, options)
|
||||
|
||||
# Private:
|
||||
setHeightInLines: (heightInLines)->
|
||||
@ -517,30 +242,6 @@ class EditorView extends View
|
||||
widthInChars ?= @calculateWidthInChars()
|
||||
@editor.setEditorWidthInChars(widthInChars) if widthInChars
|
||||
|
||||
# {Delegates to: Editor.getMaxScreenLineLength}
|
||||
getMaxScreenLineLength: -> @editor.getMaxScreenLineLength()
|
||||
|
||||
# {Delegates to: Editor.getLastScreenRow}
|
||||
getLastScreenRow: -> @editor.getLastScreenRow()
|
||||
|
||||
# {Delegates to: Editor.clipScreenPosition}
|
||||
clipScreenPosition: (screenPosition, options={}) -> @editor.clipScreenPosition(screenPosition, options)
|
||||
|
||||
# {Delegates to: Editor.screenPositionForBufferPosition}
|
||||
screenPositionForBufferPosition: (position, options) -> @editor.screenPositionForBufferPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.bufferPositionForScreenPosition}
|
||||
bufferPositionForScreenPosition: (position, options) -> @editor.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.screenRangeForBufferRange}
|
||||
screenRangeForBufferRange: (range) -> @editor.screenRangeForBufferRange(range)
|
||||
|
||||
# {Delegates to: Editor.bufferRangeForScreenRange}
|
||||
bufferRangeForScreenRange: (range) -> @editor.bufferRangeForScreenRange(range)
|
||||
|
||||
# {Delegates to: Editor.bufferRowsForScreenRows}
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @editor.bufferRowsForScreenRows(startRow, endRow)
|
||||
|
||||
# Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first.
|
||||
pageDown: ->
|
||||
newScrollTop = @scrollTop() + @scrollView[0].clientHeight
|
||||
@ -600,51 +301,9 @@ class EditorView extends View
|
||||
|
||||
# Checkout the HEAD revision of this editor's file.
|
||||
checkoutHead: ->
|
||||
if path = @getPath()
|
||||
if path = @editor.getPath()
|
||||
atom.project.getRepo()?.checkoutHead(path)
|
||||
|
||||
# {Delegates to: Editor.setText}
|
||||
setText: (text) -> @editor.setText(text)
|
||||
|
||||
# {Delegates to: Editor.save}
|
||||
save: -> @editor.save()
|
||||
|
||||
# {Delegates to: Editor.getText}
|
||||
getText: -> @editor.getText()
|
||||
|
||||
# {Delegates to: Editor.getPath}
|
||||
getPath: -> @editor?.getPath()
|
||||
|
||||
# {Delegates to: Editor.transact}
|
||||
transact: (fn) -> @editor.transact(fn)
|
||||
|
||||
# {Delegates to: TextBuffer.getLineCount}
|
||||
getLineCount: -> @getBuffer().getLineCount()
|
||||
|
||||
# {Delegates to: TextBuffer.getLastRow}
|
||||
getLastBufferRow: -> @getBuffer().getLastRow()
|
||||
|
||||
# {Delegates to: TextBuffer.getTextInRange}
|
||||
getTextInRange: (range) -> @getBuffer().getTextInRange(range)
|
||||
|
||||
# {Delegates to: TextBuffer.getEofPosition}
|
||||
getEofPosition: -> @getBuffer().getEofPosition()
|
||||
|
||||
# {Delegates to: TextBuffer.lineForRow}
|
||||
lineForBufferRow: (row) -> @getBuffer().lineForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.lineLengthForRow}
|
||||
lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.rangeForRow}
|
||||
rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.scanInRange}
|
||||
scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...)
|
||||
|
||||
# {Delegates to: TextBuffer.backwardsScanInRange}
|
||||
backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...)
|
||||
|
||||
### Internal ###
|
||||
|
||||
configure: ->
|
||||
@ -695,11 +354,11 @@ class EditorView extends View
|
||||
screenPosition = @screenPositionFromMouseEvent(e)
|
||||
if clickCount == 1
|
||||
if e.metaKey
|
||||
@addCursorAtScreenPosition(screenPosition)
|
||||
@editor.addCursorAtScreenPosition(screenPosition)
|
||||
else if e.shiftKey
|
||||
@selectToScreenPosition(screenPosition)
|
||||
@editor.selectToScreenPosition(screenPosition)
|
||||
else
|
||||
@setCursorScreenPosition(screenPosition)
|
||||
@editor.setCursorScreenPosition(screenPosition)
|
||||
else if clickCount == 2
|
||||
@editor.selectWord() unless e.shiftKey
|
||||
else if clickCount == 3
|
||||
@ -744,9 +403,9 @@ class EditorView extends View
|
||||
selectedText = @getSelectedText()
|
||||
@hiddenInput.css('width', '100%')
|
||||
@hiddenInput.on 'compositionupdate', (e) =>
|
||||
@insertText(e.originalEvent.data, {select: true, undo: 'skip'})
|
||||
@editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'})
|
||||
@hiddenInput.on 'compositionend', =>
|
||||
@insertText(selectedText, {select: true, undo: 'skip'})
|
||||
@editor.insertText(selectedText, {select: true, undo: 'skip'})
|
||||
@hiddenInput.css('width', '1px')
|
||||
|
||||
lastInput = ''
|
||||
@ -757,7 +416,7 @@ class EditorView extends View
|
||||
@selectLeft()
|
||||
|
||||
lastInput = e.originalEvent.data
|
||||
@insertText(lastInput)
|
||||
@editor.insertText(lastInput)
|
||||
@hiddenInput.val(lastInput)
|
||||
false
|
||||
|
||||
@ -768,7 +427,7 @@ class EditorView extends View
|
||||
lastMoveEvent = null
|
||||
moveHandler = (event = lastMoveEvent) =>
|
||||
if event
|
||||
@selectToScreenPosition(@screenPositionFromMouseEvent(event))
|
||||
@editor.selectToScreenPosition(@screenPositionFromMouseEvent(event))
|
||||
lastMoveEvent = event
|
||||
|
||||
$(document).on "mousemove.editor-#{@id}", moveHandler
|
||||
@ -814,6 +473,7 @@ class EditorView extends View
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
# TODO: This should be private and only called from the constructor
|
||||
edit: (editor) ->
|
||||
return if editor is @editor
|
||||
|
||||
@ -834,7 +494,7 @@ class EditorView extends View
|
||||
@showBufferConflictAlert(@editor)
|
||||
|
||||
@editor.on "path-changed.editor", =>
|
||||
@reloadGrammar()
|
||||
@editor.reloadGrammar()
|
||||
@trigger 'editor:path-changed'
|
||||
|
||||
@editor.on "grammar-changed.editor", =>
|
||||
@ -917,20 +577,15 @@ class EditorView extends View
|
||||
|
||||
### Public ###
|
||||
|
||||
# Retrieves the {Editor}'s buffer.
|
||||
#
|
||||
# Returns the current {TextBuffer}.
|
||||
getBuffer: -> @editor.buffer
|
||||
|
||||
# Scrolls the editor to the bottom.
|
||||
scrollToBottom: ->
|
||||
@scrollBottom(@getScreenLineCount() * @lineHeight)
|
||||
@scrollBottom(@editor.getScreenLineCount() * @lineHeight)
|
||||
|
||||
# Scrolls the editor to the position of the most recently added cursor.
|
||||
#
|
||||
# The editor is also centered.
|
||||
scrollToCursorPosition: ->
|
||||
@scrollToBufferPosition(@getCursorBufferPosition(), center: true)
|
||||
@scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true)
|
||||
|
||||
# Scrolls the editor to the given buffer position.
|
||||
#
|
||||
@ -966,7 +621,7 @@ class EditorView extends View
|
||||
#
|
||||
# bufferRange - The {Range} to check.
|
||||
highlightFoldsContainingBufferRange: (bufferRange) ->
|
||||
screenLines = @linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow)
|
||||
screenLines = @editor.linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow)
|
||||
for screenLine, i in screenLines
|
||||
if fold = screenLine.fold
|
||||
screenRow = @firstRenderedScreenRow + i
|
||||
@ -1180,7 +835,7 @@ class EditorView extends View
|
||||
@setHeightInLines()
|
||||
|
||||
updateLayerDimensions: ->
|
||||
height = @lineHeight * @getScreenLineCount()
|
||||
height = @lineHeight * @editor.getScreenLineCount()
|
||||
unless @layerHeight == height
|
||||
@layerHeight = height
|
||||
@underlayer.height(@layerHeight)
|
||||
@ -1189,7 +844,7 @@ class EditorView extends View
|
||||
@verticalScrollbarContent.height(@layerHeight)
|
||||
@scrollBottom(height) if @scrollBottom() > height
|
||||
|
||||
minWidth = Math.max(@charWidth * @getMaxScreenLineLength() + 20, @scrollView.width())
|
||||
minWidth = Math.max(@charWidth * @editor.getMaxScreenLineLength() + 20, @scrollView.width())
|
||||
unless @layerMinWidth == minWidth
|
||||
@renderedLines.css('min-width', minWidth)
|
||||
@underlayer.css('min-width', minWidth)
|
||||
@ -1303,9 +958,9 @@ class EditorView extends View
|
||||
|
||||
updatePlaceholderText: ->
|
||||
return unless @mini
|
||||
if (not @placeholderText) or @getText()
|
||||
if (not @placeholderText) or @editor.getText()
|
||||
@find('.placeholder-text').remove()
|
||||
else if @placeholderText and not @getText()
|
||||
else if @placeholderText and not @editor.getText()
|
||||
element = @find('.placeholder-text')
|
||||
if element.length
|
||||
element.text(@placeholderText)
|
||||
@ -1315,7 +970,7 @@ class EditorView extends View
|
||||
updateRenderedLines: ->
|
||||
firstVisibleScreenRow = @getFirstVisibleScreenRow()
|
||||
lastScreenRowToRender = firstVisibleScreenRow + @heightInLines - 1
|
||||
lastScreenRow = @getLastScreenRow()
|
||||
lastScreenRow = @editor.getLastScreenRow()
|
||||
|
||||
if @firstRenderedScreenRow? and firstVisibleScreenRow >= @firstRenderedScreenRow and lastScreenRowToRender <= @lastRenderedScreenRow
|
||||
renderFrom = Math.min(lastScreenRow, @firstRenderedScreenRow)
|
||||
@ -1344,15 +999,15 @@ class EditorView extends View
|
||||
|
||||
if change.bufferDelta?
|
||||
afterStart = change.end + change.bufferDelta + 1
|
||||
if @lineForBufferRow(afterStart) is ''
|
||||
if @editor.lineForBufferRow(afterStart) is ''
|
||||
afterEnd = afterStart
|
||||
afterEnd++ while @lineForBufferRow(afterEnd + 1) is ''
|
||||
afterEnd++ while @editor.lineForBufferRow(afterEnd + 1) is ''
|
||||
emptyLineChanges.push({start: afterStart, end: afterEnd, screenDelta: 0})
|
||||
|
||||
beforeEnd = change.start - 1
|
||||
if @lineForBufferRow(beforeEnd) is ''
|
||||
if @editor.lineForBufferRow(beforeEnd) is ''
|
||||
beforeStart = beforeEnd
|
||||
beforeStart-- while @lineForBufferRow(beforeStart - 1) is ''
|
||||
beforeStart-- while @editor.lineForBufferRow(beforeStart - 1) is ''
|
||||
emptyLineChanges.push({start: beforeStart, end: beforeEnd, screenDelta: 0})
|
||||
|
||||
emptyLineChanges
|
||||
@ -1461,7 +1116,7 @@ class EditorView extends View
|
||||
@renderedLines.css('padding-top', paddingTop)
|
||||
@gutter.lineNumbers.css('padding-top', paddingTop)
|
||||
|
||||
paddingBottom = (@getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight
|
||||
paddingBottom = (@editor.getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight
|
||||
@renderedLines.css('padding-bottom', paddingBottom)
|
||||
@gutter.lineNumbers.css('padding-bottom', paddingBottom)
|
||||
|
||||
@ -1480,7 +1135,7 @@ class EditorView extends View
|
||||
# Returns a {Number}.
|
||||
getLastVisibleScreenRow: ->
|
||||
calculatedRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1
|
||||
screenRow = Math.max(0, Math.min(@getScreenLineCount() - 1, calculatedRow))
|
||||
screenRow = Math.max(0, Math.min(@editor.getScreenLineCount() - 1, calculatedRow))
|
||||
screenRow = 0 if isNaN(screenRow)
|
||||
screenRow
|
||||
|
||||
@ -1585,7 +1240,7 @@ class EditorView extends View
|
||||
#
|
||||
# Returns an object with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForBufferPosition: (position) ->
|
||||
@pixelPositionForScreenPosition(@screenPositionForBufferPosition(position))
|
||||
@pixelPositionForScreenPosition(@editor.screenPositionForBufferPosition(position))
|
||||
|
||||
# Converts a screen position to a pixel position.
|
||||
#
|
||||
@ -1734,31 +1389,15 @@ class EditorView extends View
|
||||
return if @mini
|
||||
|
||||
@highlightedLine?.removeClass('cursor-line')
|
||||
if @getSelection().isEmpty()
|
||||
@highlightedLine = @lineElementForScreenRow(@getCursorScreenRow())
|
||||
if @editor.getSelection().isEmpty()
|
||||
@highlightedLine = @lineElementForScreenRow(@editor.getCursorScreenRow())
|
||||
@highlightedLine.addClass('cursor-line')
|
||||
else
|
||||
@highlightedLine = null
|
||||
|
||||
# {Delegates to: Editor.getGrammar}
|
||||
getGrammar: ->
|
||||
@editor.getGrammar()
|
||||
|
||||
# {Delegates to: Editor.setGrammar}
|
||||
setGrammar: (grammar) ->
|
||||
@editor.setGrammar(grammar)
|
||||
|
||||
# {Delegates to: Editor.reloadGrammar}
|
||||
reloadGrammar: ->
|
||||
@editor.reloadGrammar()
|
||||
|
||||
# {Delegates to: Editor.scopesForBufferPosition}
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@editor.scopesForBufferPosition(bufferPosition)
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToPasteboard: ->
|
||||
path = @getPath()
|
||||
path = @editor.getPath()
|
||||
atom.pasteboard.write(path) if path?
|
||||
|
||||
### Internal ###
|
||||
@ -1845,13 +1484,13 @@ class EditorView extends View
|
||||
' '
|
||||
|
||||
replaceSelectedText: (replaceFn) ->
|
||||
selection = @getSelection()
|
||||
selection = @editor.getSelection()
|
||||
return false if selection.isEmpty()
|
||||
|
||||
text = replaceFn(@getTextInRange(selection.getBufferRange()))
|
||||
text = replaceFn(@editor.getTextInRange(selection.getBufferRange()))
|
||||
return false if text is null or text is undefined
|
||||
|
||||
@insertText(text, select: true)
|
||||
@editor.insertText(text, select: true)
|
||||
true
|
||||
|
||||
consolidateSelections: (e) -> e.abortKeyBinding() unless @editor.consolidateSelections()
|
||||
@ -1859,12 +1498,6 @@ class EditorView extends View
|
||||
logCursorScope: ->
|
||||
console.log @editor.getCursorScopes()
|
||||
|
||||
beginTransaction: -> @editor.beginTransaction()
|
||||
|
||||
commitTransaction: -> @editor.commitTransaction()
|
||||
|
||||
abortTransaction: -> @editor.abortTransaction()
|
||||
|
||||
logScreenLines: (start, end) ->
|
||||
@editor.logScreenLines(start, end)
|
||||
|
||||
|
@ -930,7 +930,7 @@ class Editor extends Model
|
||||
|
||||
# Public:
|
||||
#
|
||||
# FIXME: What does this do?
|
||||
# Removes all but one cursor (if there are multiple cursors)
|
||||
consolidateSelections: ->
|
||||
selections = @getSelections()
|
||||
if selections.length > 1
|
||||
|
@ -34,21 +34,22 @@ class Gutter extends View
|
||||
|
||||
handleMouseEvents: (e) ->
|
||||
editorView = @getEditorView()
|
||||
editor = @getEditor()
|
||||
startRow = editorView.screenPositionFromMouseEvent(e).row
|
||||
if e.shiftKey
|
||||
editorView.selectToScreenPosition([startRow + 1, 0])
|
||||
editor.selectToScreenPosition([startRow + 1, 0])
|
||||
return
|
||||
else
|
||||
editorView.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
|
||||
editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
|
||||
|
||||
moveHandler = (e) =>
|
||||
start = startRow
|
||||
end = editorView.screenPositionFromMouseEvent(e).row
|
||||
if end > start then end++ else start++
|
||||
editorView.getSelection().setScreenRange([[start, 0], [end, 0]])
|
||||
editor.getSelection().setScreenRange([[start, 0], [end, 0]])
|
||||
|
||||
$(document).on "mousemove.gutter-#{@getEditorView().id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{@getEditorView().id}", => $(document).off 'mousemove', moveHandler
|
||||
$(document).on "mousemove.gutter-#{editorView.id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{editorView.id}", => $(document).off 'mousemove', moveHandler
|
||||
|
||||
### Public ###
|
||||
|
||||
@ -58,6 +59,9 @@ class Gutter extends View
|
||||
getEditorView: ->
|
||||
@parentView
|
||||
|
||||
getEditor: ->
|
||||
@getEditorView().getEditor()
|
||||
|
||||
# Defines whether to show the gutter or not.
|
||||
#
|
||||
# showLineNumbers - A {Boolean} which, if `false`, hides the gutter
|
||||
@ -192,9 +196,9 @@ class Gutter extends View
|
||||
@elementBuilder.children
|
||||
|
||||
buildLineElementsHtml: (startScreenRow, endScreenRow) =>
|
||||
editorView = @getEditorView()
|
||||
maxDigits = editorView.getLineCount().toString().length
|
||||
rows = editorView.bufferRowsForScreenRows(startScreenRow, endScreenRow)
|
||||
editor = @getEditor()
|
||||
maxDigits = editor.getLineCount().toString().length
|
||||
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
|
||||
|
||||
html = ''
|
||||
for row in rows
|
||||
@ -204,7 +208,7 @@ class Gutter extends View
|
||||
rowValue = (row + 1).toString()
|
||||
|
||||
classes = "line-number line-number-#{row}"
|
||||
classes += ' fold' if editorView.isFoldedAtBufferRow(row)
|
||||
classes += ' fold' if editor.isFoldedAtBufferRow(row)
|
||||
|
||||
rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length)
|
||||
|
||||
@ -230,10 +234,11 @@ class Gutter extends View
|
||||
@highlightedLineNumbers.push(highlightedLineNumber)
|
||||
|
||||
highlightLines: ->
|
||||
return unless @getEditorView().editor?.isAlive()
|
||||
editor = @getEditor()
|
||||
return unless editor?.isAlive()
|
||||
|
||||
if @getEditorView().getSelection().isEmpty()
|
||||
row = @getEditorView().getCursorScreenPosition().row
|
||||
if editor.getSelection().isEmpty()
|
||||
row = editor.getCursorScreenPosition().row
|
||||
rowRange = new Range([row, 0], [row, 0])
|
||||
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
|
||||
|
||||
@ -242,7 +247,7 @@ class Gutter extends View
|
||||
@highlightedRows = rowRange
|
||||
@selectionEmpty = true
|
||||
else
|
||||
selectedRows = @getEditorView().getSelection().getScreenRange()
|
||||
selectedRows = editor.getSelection().getScreenRange()
|
||||
endRow = selectedRows.end.row
|
||||
endRow-- if selectedRows.end.column is 0
|
||||
selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0])
|
||||
|
@ -1,4 +1,4 @@
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
{$, View} = require './space-pen-extensions'
|
||||
PaneView = require './pane-view'
|
||||
PaneContainer = require './pane-container'
|
||||
@ -6,11 +6,9 @@ PaneContainer = require './pane-container'
|
||||
# Private: Manages the list of panes within a {WorkspaceView}
|
||||
module.exports =
|
||||
class PaneContainerView extends View
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
Delegator.includeInto(this)
|
||||
|
||||
@deserialize: (state) ->
|
||||
new this(PaneContainer.deserialize(state.model))
|
||||
@delegatesMethod 'saveAll', toProperty: 'model'
|
||||
|
||||
@content: ->
|
||||
@div class: 'panes'
|
||||
@ -29,14 +27,8 @@ class PaneContainerView extends View
|
||||
viewClass = model.getViewClass()
|
||||
model._view ?= new viewClass(model)
|
||||
|
||||
serializeParams: ->
|
||||
model: @model.serialize()
|
||||
|
||||
### Public ###
|
||||
|
||||
itemDestroyed: (item) ->
|
||||
@trigger 'item-destroyed', [item]
|
||||
|
||||
getRoot: ->
|
||||
@children().first().view()
|
||||
|
||||
@ -65,9 +57,6 @@ class PaneContainerView extends View
|
||||
@setRoot(null)
|
||||
@trigger 'pane:removed', [child] if child instanceof PaneView
|
||||
|
||||
saveAll: ->
|
||||
pane.saveItems() for pane in @getPanes()
|
||||
|
||||
confirmClose: ->
|
||||
saved = true
|
||||
for pane in @getPanes()
|
||||
@ -105,28 +94,10 @@ class PaneContainerView extends View
|
||||
@getActivePane()?.activeView
|
||||
|
||||
paneForUri: (uri) ->
|
||||
for pane in @getPanes()
|
||||
view = pane.itemForUri(uri)
|
||||
return pane if view?
|
||||
null
|
||||
@viewForModel(@model.paneForUri(uri))
|
||||
|
||||
focusNextPane: ->
|
||||
panes = @getPanes()
|
||||
if panes.length > 1
|
||||
currentIndex = panes.indexOf(@getFocusedPane())
|
||||
nextIndex = (currentIndex + 1) % panes.length
|
||||
panes[nextIndex].focus()
|
||||
true
|
||||
else
|
||||
false
|
||||
@model.activateNextPane()
|
||||
|
||||
focusPreviousPane: ->
|
||||
panes = @getPanes()
|
||||
if panes.length > 1
|
||||
currentIndex = panes.indexOf(@getFocusedPane())
|
||||
previousIndex = currentIndex - 1
|
||||
previousIndex = panes.length - 1 if previousIndex < 0
|
||||
panes[previousIndex].focus()
|
||||
true
|
||||
else
|
||||
false
|
||||
@model.activatePreviousPane()
|
||||
|
@ -1,3 +1,4 @@
|
||||
{find} = require 'underscore-plus'
|
||||
{Model} = require 'theorist'
|
||||
Serializable = require 'serializable'
|
||||
Pane = require './pane'
|
||||
@ -36,14 +37,32 @@ class PaneContainer extends Model
|
||||
getPanes: ->
|
||||
@root?.getPanes() ? []
|
||||
|
||||
paneForUri: (uri) ->
|
||||
find @getPanes(), (pane) -> pane.itemForUri(uri)?
|
||||
|
||||
saveAll: ->
|
||||
pane.saveItems() for pane in @getPanes()
|
||||
|
||||
activateNextPane: ->
|
||||
panes = @getPanes()
|
||||
if panes.length > 1
|
||||
currentIndex = panes.indexOf(@activePane)
|
||||
nextIndex = (currentIndex + 1) % panes.length
|
||||
panes[nextIndex].activate()
|
||||
true
|
||||
else
|
||||
@activePane = null
|
||||
false
|
||||
|
||||
activatePreviousPane: ->
|
||||
panes = @getPanes()
|
||||
if panes.length > 1
|
||||
currentIndex = panes.indexOf(@activePane)
|
||||
previousIndex = currentIndex - 1
|
||||
previousIndex = panes.length - 1 if previousIndex < 0
|
||||
panes[previousIndex].activate()
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
onRootChanged: (root) =>
|
||||
@unsubscribe(@previousRoot) if @previousRoot?
|
||||
@ -64,3 +83,6 @@ class PaneContainer extends Model
|
||||
|
||||
destroyEmptyPanes: ->
|
||||
pane.destroy() for pane in @getPanes() when pane.items.length is 0
|
||||
|
||||
itemDestroyed: (item) ->
|
||||
@emit 'item-destroyed', item
|
||||
|
@ -1,307 +0,0 @@
|
||||
{find, compact, extend} = require 'underscore-plus'
|
||||
{dirname} = require 'path'
|
||||
{Model, Sequence} = require 'theorist'
|
||||
Serializable = require 'serializable'
|
||||
PaneAxis = require './pane-axis'
|
||||
PaneView = null
|
||||
|
||||
# Public: A container for multiple items, one of which is *active* at a given
|
||||
# time. With the default packages, a tab is displayed for each item and the
|
||||
# active item's view is displayed.
|
||||
module.exports =
|
||||
class PaneModel extends Model
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
|
||||
@properties
|
||||
container: null
|
||||
activeItem: null
|
||||
focused: false
|
||||
|
||||
# Public: Only one pane is considered *active* at a time. A pane is activated
|
||||
# when it is focused, and when focus returns to the pane container after
|
||||
# moving to another element such as a panel, it returns to the active pane.
|
||||
@behavior 'active', ->
|
||||
@$container
|
||||
.switch((container) -> container?.$activePane)
|
||||
.map((activePane) => activePane is this)
|
||||
.distinctUntilChanged()
|
||||
|
||||
# Private:
|
||||
constructor: (params) ->
|
||||
super
|
||||
|
||||
@items = Sequence.fromArray(params?.items ? [])
|
||||
@activeItem ?= @items[0]
|
||||
|
||||
@subscribe @items.onEach (item) =>
|
||||
if typeof item.on is 'function'
|
||||
@subscribe item, 'destroyed', => @removeItem(item)
|
||||
|
||||
@subscribe @items.onRemoval (item, index) =>
|
||||
@unsubscribe item if typeof item.on is 'function'
|
||||
|
||||
@activate() if params?.active
|
||||
|
||||
# Private: Called by the Serializable mixin during serialization.
|
||||
serializeParams: ->
|
||||
items: compact(@items.map((item) -> item.serialize?()))
|
||||
activeItemUri: @activeItem?.getUri?()
|
||||
focused: @focused
|
||||
active: @active
|
||||
|
||||
# Private: Called by the Serializable mixin during deserialization.
|
||||
deserializeParams: (params) ->
|
||||
{items, activeItemUri} = params
|
||||
params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState))
|
||||
params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri
|
||||
params
|
||||
|
||||
# Private: Called by the view layer to construct a view for this model.
|
||||
getViewClass: -> PaneView ?= require './pane-view'
|
||||
|
||||
isActive: -> @active
|
||||
|
||||
# Private: Called by the view layer to indicate that the pane has gained focus.
|
||||
focus: ->
|
||||
@focused = true
|
||||
@activate() unless @isActive()
|
||||
|
||||
# Private: Called by the view layer to indicate that the pane has lost focus.
|
||||
blur: ->
|
||||
@focused = false
|
||||
true # if this is called from an event handler, don't cancel it
|
||||
|
||||
# Public: Makes this pane the *active* pane, causing it to gain focus
|
||||
# immediately.
|
||||
activate: ->
|
||||
@container?.activePane = this
|
||||
@emit 'activated'
|
||||
|
||||
# Private:
|
||||
getPanes: -> [this]
|
||||
|
||||
# Public:
|
||||
getItems: ->
|
||||
@items.slice()
|
||||
|
||||
# Public: Returns the item at the specified index.
|
||||
itemAtIndex: (index) ->
|
||||
@items[index]
|
||||
|
||||
# Public: Makes the next item active.
|
||||
activateNextItem: ->
|
||||
index = @getActiveItemIndex()
|
||||
if index < @items.length - 1
|
||||
@activateItemAtIndex(index + 1)
|
||||
else
|
||||
@activateItemAtIndex(0)
|
||||
|
||||
# Public: Makes the previous item active.
|
||||
activatePreviousItem: ->
|
||||
index = @getActiveItemIndex()
|
||||
if index > 0
|
||||
@activateItemAtIndex(index - 1)
|
||||
else
|
||||
@activateItemAtIndex(@items.length - 1)
|
||||
|
||||
# Public: Returns the index of the current active item.
|
||||
getActiveItemIndex: ->
|
||||
@items.indexOf(@activeItem)
|
||||
|
||||
# Public: Makes the item at the given index active.
|
||||
activateItemAtIndex: (index) ->
|
||||
@activateItem(@itemAtIndex(index))
|
||||
|
||||
# Public: Makes the given item active, adding the item if necessary.
|
||||
activateItem: (item) ->
|
||||
if item?
|
||||
@addItem(item)
|
||||
@activeItem = item
|
||||
|
||||
# Public: Adds the item to the pane.
|
||||
#
|
||||
# * item:
|
||||
# The item to add. It can be a model with an associated view or a view.
|
||||
# * index:
|
||||
# An optional index at which to add the item. If omitted, the item is
|
||||
# added to the end.
|
||||
#
|
||||
# Returns the added item
|
||||
addItem: (item, index=@getActiveItemIndex() + 1) ->
|
||||
return if item in @items
|
||||
|
||||
@items.splice(index, 0, item)
|
||||
@emit 'item-added', item, index
|
||||
item
|
||||
|
||||
# Private:
|
||||
removeItem: (item, destroying) ->
|
||||
index = @items.indexOf(item)
|
||||
return if index is -1
|
||||
@activateNextItem() if item is @activeItem and @items.length > 1
|
||||
@items.splice(index, 1)
|
||||
@emit 'item-removed', item, index, destroying
|
||||
@destroy() if @items.length is 0
|
||||
|
||||
# Public: Moves the given item to the specified index.
|
||||
moveItem: (item, newIndex) ->
|
||||
oldIndex = @items.indexOf(item)
|
||||
@items.splice(oldIndex, 1)
|
||||
@items.splice(newIndex, 0, item)
|
||||
@emit 'item-moved', item, newIndex
|
||||
|
||||
# Public: Moves the given item to the given index at another pane.
|
||||
moveItemToPane: (item, pane, index) ->
|
||||
pane.addItem(item, index)
|
||||
@removeItem(item)
|
||||
|
||||
# Public: Destroys the currently active item and make the next item active.
|
||||
destroyActiveItem: ->
|
||||
@destroyItem(@activeItem)
|
||||
false
|
||||
|
||||
# Public: Destroys the given item. If it is the active item, activate the next
|
||||
# one. If this is the last item, also destroys the pane.
|
||||
destroyItem: (item) ->
|
||||
@emit 'before-item-destroyed', item
|
||||
if @promptToSaveItem(item)
|
||||
@emit 'item-destroyed', item
|
||||
@removeItem(item, true)
|
||||
item.destroy?()
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
# Public: Destroys all items and destroys the pane.
|
||||
destroyItems: ->
|
||||
@destroyItem(item) for item in @getItems()
|
||||
|
||||
# Public: Destroys all items but the active one.
|
||||
destroyInactiveItems: ->
|
||||
@destroyItem(item) for item in @getItems() when item isnt @activeItem
|
||||
|
||||
# Private: Called by model superclass.
|
||||
destroyed: ->
|
||||
@container.activateNextPane() if @isActive()
|
||||
item.destroy?() for item in @items.slice()
|
||||
|
||||
# Public: Prompts the user to save the given item if it can be saved and is
|
||||
# currently unsaved.
|
||||
promptToSaveItem: (item) ->
|
||||
return true unless item.shouldPromptToSave?()
|
||||
|
||||
uri = item.getUri()
|
||||
chosen = atom.confirm
|
||||
message: "'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
detailedMessage: "Your changes will be lost if you close this item without saving."
|
||||
buttons: ["Save", "Cancel", "Don't Save"]
|
||||
|
||||
switch chosen
|
||||
when 0 then @saveItem(item, -> true)
|
||||
when 1 then false
|
||||
when 2 then true
|
||||
|
||||
# Public: Saves the active item.
|
||||
saveActiveItem: ->
|
||||
@saveItem(@activeItem)
|
||||
|
||||
# Public: Saves the active item at a prompted-for location.
|
||||
saveActiveItemAs: ->
|
||||
@saveItemAs(@activeItem)
|
||||
|
||||
# Public: Saves the specified item.
|
||||
#
|
||||
# * item: The item to save.
|
||||
# * nextAction: An optional function which will be called after the item is saved.
|
||||
saveItem: (item, nextAction) ->
|
||||
if item.getUri?()
|
||||
item.save?()
|
||||
nextAction?()
|
||||
else
|
||||
@saveItemAs(item, nextAction)
|
||||
|
||||
# Public: Saves the given item at a prompted-for location.
|
||||
#
|
||||
# * item: The item to save.
|
||||
# * nextAction: An optional function which will be called after the item is saved.
|
||||
saveItemAs: (item, nextAction) ->
|
||||
return unless item.saveAs?
|
||||
|
||||
itemPath = item.getPath?()
|
||||
itemPath = dirname(itemPath) if itemPath
|
||||
path = atom.showSaveDialogSync(itemPath)
|
||||
if path
|
||||
item.saveAs(path)
|
||||
nextAction?()
|
||||
|
||||
# Public: Saves all items.
|
||||
saveItems: ->
|
||||
@saveItem(item) for item in @getItems()
|
||||
|
||||
# Public: Returns the first item that matches the given URI or undefined if
|
||||
# none exists.
|
||||
itemForUri: (uri) ->
|
||||
find @items, (item) -> item.getUri?() is uri
|
||||
|
||||
# Public: Activates the first item that matches the given URI. Returns a
|
||||
# boolean indicating whether a matching item was found.
|
||||
activateItemForUri: (uri) ->
|
||||
if item = @itemForUri(uri)
|
||||
@activateItem(item)
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
# Private:
|
||||
copyActiveItem: ->
|
||||
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
|
||||
|
||||
# Public: Creates a new pane to the left of the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {PaneModel}.
|
||||
splitLeft: (params) ->
|
||||
@split('horizontal', 'before', params)
|
||||
|
||||
# Public: Creates a new pane to the right of the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {PaneModel}.
|
||||
splitRight: (params) ->
|
||||
@split('horizontal', 'after', params)
|
||||
|
||||
# Public: Creates a new pane above the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {PaneModel}.
|
||||
splitUp: (params) ->
|
||||
@split('vertical', 'before', params)
|
||||
|
||||
# Public: Creates a new pane below the receiver.
|
||||
#
|
||||
# * params:
|
||||
# + items: An optional array of items with which to construct the new pane.
|
||||
#
|
||||
# Returns the new {PaneModel}.
|
||||
splitDown: (params) ->
|
||||
@split('vertical', 'after', params)
|
||||
|
||||
# Private:
|
||||
split: (orientation, side, params) ->
|
||||
if @parent.orientation isnt orientation
|
||||
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]}))
|
||||
|
||||
newPane = new @constructor(extend({focused: true}, params))
|
||||
switch side
|
||||
when 'before' then @parent.insertChildBefore(this, newPane)
|
||||
when 'after' then @parent.insertChildAfter(this, newPane)
|
||||
|
||||
newPane.activate()
|
||||
newPane
|
@ -1,6 +1,6 @@
|
||||
{$, View} = require './space-pen-extensions'
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
PropertyAccessors = require 'property-accessors'
|
||||
|
||||
Pane = require './pane'
|
||||
|
||||
@ -12,14 +12,11 @@ Pane = require './pane'
|
||||
# building a package that deals with switching between panes or tiems.
|
||||
module.exports =
|
||||
class PaneView extends View
|
||||
Serializable.includeInto(this)
|
||||
Delegator.includeInto(this)
|
||||
PropertyAccessors.includeInto(this)
|
||||
|
||||
@version: 1
|
||||
|
||||
@deserialize: (state) ->
|
||||
new this(Pane.deserialize(state.model))
|
||||
|
||||
@content: (wrappedView) ->
|
||||
@div class: 'pane', tabindex: -1, =>
|
||||
@div class: 'item-views', outlet: 'itemViews'
|
||||
@ -47,14 +44,11 @@ class PaneView extends View
|
||||
@handleEvents()
|
||||
|
||||
handleEvents: ->
|
||||
@subscribe @model, 'destroyed', => @remove()
|
||||
|
||||
@subscribe @model.$activeItem, @onActiveItemChanged
|
||||
@subscribe @model, 'item-added', @onItemAdded
|
||||
@subscribe @model, 'item-removed', @onItemRemoved
|
||||
@subscribe @model, 'item-moved', @onItemMoved
|
||||
@subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed
|
||||
@subscribe @model, 'item-destroyed', @onItemDestroyed
|
||||
@subscribe @model, 'activated', @onActivated
|
||||
@subscribe @model.$active, @onActiveStatusChanged
|
||||
|
||||
@ -85,13 +79,6 @@ class PaneView extends View
|
||||
@command 'pane:close', => @destroyItems()
|
||||
@command 'pane:close-other-items', => @destroyInactiveItems()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.model = Pane.deserialize(params.model)
|
||||
params
|
||||
|
||||
serializeParams: ->
|
||||
model: @model.serialize()
|
||||
|
||||
# Deprecated: Use ::destroyItem
|
||||
removeItem: (item) -> @destroyItem(item)
|
||||
|
||||
@ -153,7 +140,6 @@ class PaneView extends View
|
||||
view.show() if @attached
|
||||
view.focus() if hasFocus
|
||||
|
||||
@activeView = view
|
||||
@trigger 'pane:active-item-changed', [item]
|
||||
|
||||
onItemAdded: (item, index) =>
|
||||
@ -166,7 +152,6 @@ class PaneView extends View
|
||||
@viewsByItem.delete(item)
|
||||
|
||||
if viewToRemove?
|
||||
viewToRemove.setModel?(null)
|
||||
if destroyed
|
||||
viewToRemove.remove()
|
||||
else
|
||||
@ -181,15 +166,13 @@ class PaneView extends View
|
||||
@unsubscribe(item) if typeof item.off is 'function'
|
||||
@trigger 'pane:before-item-destroyed', [item]
|
||||
|
||||
onItemDestroyed: (item) =>
|
||||
@getContainer()?.itemDestroyed(item)
|
||||
|
||||
# Private:
|
||||
activeItemTitleChanged: =>
|
||||
@trigger 'pane:active-item-title-changed'
|
||||
|
||||
# Private:
|
||||
viewForItem: (item) ->
|
||||
return unless item?
|
||||
if item instanceof $
|
||||
item
|
||||
else if view = @viewsByItem.get(item)
|
||||
@ -201,8 +184,7 @@ class PaneView extends View
|
||||
view
|
||||
|
||||
# Private:
|
||||
viewForActiveItem: ->
|
||||
@viewForItem(@activeItem)
|
||||
@::accessor 'activeView', -> @viewForItem(@activeItem)
|
||||
|
||||
splitLeft: (items...) -> @model.splitLeft({items})._view
|
||||
|
||||
|
@ -36,7 +36,7 @@ class Pane extends Model
|
||||
|
||||
@subscribe @items.onEach (item) =>
|
||||
if typeof item.on is 'function'
|
||||
@subscribe item, 'destroyed', => @removeItem(item)
|
||||
@subscribe item, 'destroyed', => @removeItem(item, true)
|
||||
|
||||
@subscribe @items.onRemoval (item, index) =>
|
||||
@unsubscribe item if typeof item.on is 'function'
|
||||
@ -142,6 +142,7 @@ class Pane extends Model
|
||||
@activateNextItem() if item is @activeItem and @items.length > 1
|
||||
@items.splice(index, 1)
|
||||
@emit 'item-removed', item, index, destroying
|
||||
@container?.itemDestroyed(item) if destroying
|
||||
@destroy() if @items.length is 0
|
||||
|
||||
# Public: Moves the given item to the specified index.
|
||||
@ -166,7 +167,6 @@ class Pane extends Model
|
||||
destroyItem: (item) ->
|
||||
@emit 'before-item-destroyed', item
|
||||
if @promptToSaveItem(item)
|
||||
@emit 'item-destroyed', item
|
||||
@removeItem(item, true)
|
||||
item.destroy?()
|
||||
true
|
||||
|
@ -30,7 +30,7 @@ class SelectList extends View
|
||||
# This method can be overridden by subclasses but `super` should always
|
||||
# be called.
|
||||
initialize: ->
|
||||
@miniEditor.getBuffer().on 'changed', => @schedulePopulateList()
|
||||
@miniEditor.getEditor().getBuffer().on 'changed', => @schedulePopulateList()
|
||||
@miniEditor.hiddenInput.on 'focusout', => @cancel() unless @cancelling
|
||||
@on 'core:move-up', => @selectPreviousItem()
|
||||
@on 'core:move-down', => @selectNextItem()
|
||||
@ -98,7 +98,7 @@ class SelectList extends View
|
||||
#
|
||||
# Returns a {String} to use when fuzzy filtering the elements to display.
|
||||
getFilterQuery: ->
|
||||
@miniEditor.getText()
|
||||
@miniEditor.getEditor().getText()
|
||||
|
||||
# Public: Build the DOM elements using the array from the last call to
|
||||
# {.setArray}.
|
||||
@ -207,7 +207,7 @@ class SelectList extends View
|
||||
|
||||
# Private:
|
||||
cancelled: ->
|
||||
@miniEditor.setText('')
|
||||
@miniEditor.getEditor().setText('')
|
||||
@miniEditor.updateDisplay()
|
||||
|
||||
# Public: Cancel and close the select list dialog.
|
||||
|
@ -1,10 +1,11 @@
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
Q = require 'q'
|
||||
{$, $$, View} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
Delegator = require 'delegato'
|
||||
{$, $$, View} = require './space-pen-extensions'
|
||||
fs = require 'fs-plus'
|
||||
Serializable = require 'serializable'
|
||||
Workspace = require './workspace'
|
||||
EditorView = require './editor-view'
|
||||
PaneView = require './pane-view'
|
||||
PaneColumnView = require './pane-column-view'
|
||||
@ -38,10 +39,14 @@ Editor = require './editor'
|
||||
#
|
||||
module.exports =
|
||||
class WorkspaceView extends View
|
||||
Serializable.includeInto(this)
|
||||
atom.deserializers.add(this, PaneView, PaneRowView, PaneColumnView, EditorView)
|
||||
Delegator.includeInto(this)
|
||||
|
||||
@version: 2
|
||||
@delegatesProperty 'fullScreen', 'destroyedItemUris', toProperty: 'model'
|
||||
@delegatesMethods 'open', 'openSync', 'openSingletonSync', 'reopenItemSync',
|
||||
'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem',
|
||||
toProperty: 'model'
|
||||
|
||||
@version: 4
|
||||
|
||||
@configDefaults:
|
||||
ignoredNames: [".git", ".svn", ".DS_Store"]
|
||||
@ -59,13 +64,14 @@ class WorkspaceView extends View
|
||||
@div class: 'panes', outlet: 'panes'
|
||||
|
||||
# Private:
|
||||
initialize: ({panes, @fullScreen}={}) ->
|
||||
panes ?= new PaneContainerView
|
||||
initialize: (@model) ->
|
||||
@model ?= new Workspace
|
||||
|
||||
panes = new PaneContainerView(@model.paneContainer)
|
||||
@panes.replaceWith(panes)
|
||||
@panes = panes
|
||||
|
||||
@destroyedItemUris = []
|
||||
@subscribe @panes, 'item-destroyed', @onPaneItemDestroyed
|
||||
@subscribe @model, 'uri-opened', => @trigger 'uri-opened'
|
||||
|
||||
@updateTitle()
|
||||
|
||||
@ -116,16 +122,6 @@ class WorkspaceView extends View
|
||||
@command 'core:save', => @saveActivePaneItem()
|
||||
@command 'core:save-as', => @saveActivePaneItemAs()
|
||||
|
||||
# Private:
|
||||
deserializeParams: (params) ->
|
||||
params.panes = atom.deserializers.deserialize(params.panes)
|
||||
params
|
||||
|
||||
# Private:
|
||||
serializeParams: ->
|
||||
panes: @panes.serialize()
|
||||
fullScreen: atom.isFullScreen()
|
||||
|
||||
# Private:
|
||||
handleFocus: (e) ->
|
||||
if @getActivePane()
|
||||
@ -149,81 +145,6 @@ class WorkspaceView extends View
|
||||
confirmClose: ->
|
||||
@panes.confirmClose()
|
||||
|
||||
# Public: Asynchronously opens a given a filepath in Atom.
|
||||
#
|
||||
# * filePath: A file path
|
||||
# * options
|
||||
# + initialLine: The buffer line number to open to.
|
||||
#
|
||||
# Returns a promise that resolves to the {Editor} for the file URI.
|
||||
open: (filePath, options={}) ->
|
||||
changeFocus = options.changeFocus ? true
|
||||
filePath = atom.project.resolve(filePath)
|
||||
initialLine = options.initialLine
|
||||
activePane = @getActivePane()
|
||||
|
||||
editor = activePane.itemForUri(atom.project.relativize(filePath)) if activePane and filePath
|
||||
promise = atom.project.open(filePath, {initialLine}) if not editor
|
||||
|
||||
Q(editor ? promise)
|
||||
.then (editor) =>
|
||||
if not activePane
|
||||
activePane = new PaneView(editor)
|
||||
@panes.setRoot(activePane)
|
||||
|
||||
@itemOpened(editor)
|
||||
activePane.activateItem(editor)
|
||||
activePane.activate() if changeFocus
|
||||
@trigger "uri-opened"
|
||||
editor
|
||||
.catch (error) ->
|
||||
console.error(error.stack ? error)
|
||||
|
||||
# Private: Only used in specs
|
||||
openSync: (uri, {changeFocus, initialLine, pane, split}={}) ->
|
||||
changeFocus ?= true
|
||||
pane ?= @getActivePane()
|
||||
uri = atom.project.relativize(uri)
|
||||
|
||||
if pane
|
||||
if uri
|
||||
paneItem = pane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine})
|
||||
else
|
||||
paneItem = atom.project.openSync()
|
||||
|
||||
if split == 'right'
|
||||
panes = @getPanes()
|
||||
if panes.length == 1
|
||||
pane = panes[0].splitRight()
|
||||
else
|
||||
pane = _.last(panes)
|
||||
else if split == 'left'
|
||||
pane = @getPanes()[0]
|
||||
|
||||
pane.activateItem(paneItem)
|
||||
else
|
||||
paneItem = atom.project.openSync(uri, {initialLine})
|
||||
pane = new PaneView(paneItem)
|
||||
@panes.setRoot(pane)
|
||||
|
||||
@itemOpened(paneItem)
|
||||
|
||||
pane.activate() if changeFocus
|
||||
paneItem
|
||||
|
||||
openSingletonSync: (uri, {changeFocus, initialLine, split}={}) ->
|
||||
changeFocus ?= true
|
||||
uri = atom.project.relativize(uri)
|
||||
pane = @panes.paneForUri(uri)
|
||||
|
||||
if pane
|
||||
paneItem = pane.itemForUri(uri)
|
||||
pane.activateItem(paneItem)
|
||||
pane.activate() if changeFocus
|
||||
paneItem
|
||||
else
|
||||
@openSync(uri, {changeFocus, initialLine, split})
|
||||
|
||||
# Public: Updates the application's title, based on whichever file is open.
|
||||
updateTitle: ->
|
||||
if projectPath = atom.project.getPath()
|
||||
@ -242,22 +163,6 @@ class WorkspaceView extends View
|
||||
getEditorViews: ->
|
||||
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
|
||||
|
||||
# Private: Retrieves all of the modified buffers that are open and unsaved.
|
||||
#
|
||||
# Returns an {Array} of {TextBuffer}s.
|
||||
getModifiedBuffers: ->
|
||||
modifiedBuffers = []
|
||||
for pane in @getPanes()
|
||||
for item in pane.getItems() when item instanceof Editor
|
||||
modifiedBuffers.push item.buffer if item.buffer.isModified()
|
||||
modifiedBuffers
|
||||
|
||||
# Private: Retrieves all of the paths to open files.
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getOpenBufferPaths: ->
|
||||
_.uniq(_.flatten(@getEditorViews().map (editorView) -> editorView.getOpenBufferPaths()))
|
||||
|
||||
# Public: Prepends the element to the top of the window.
|
||||
prependToTop: (element) ->
|
||||
@vertical.prepend(element)
|
||||
@ -296,39 +201,23 @@ class WorkspaceView extends View
|
||||
|
||||
# Public: Returns the currently focused item from within the focused {PaneView}
|
||||
getActivePaneItem: ->
|
||||
@panes.getActivePaneItem()
|
||||
@model.activePaneItem
|
||||
|
||||
# Public: Returns the view of the currently focused item.
|
||||
getActiveView: ->
|
||||
@panes.getActiveView()
|
||||
|
||||
# Public: destroy/close the active item.
|
||||
destroyActivePaneItem: ->
|
||||
@getActivePane()?.destroyActiveItem()
|
||||
|
||||
# Public: save the active item.
|
||||
saveActivePaneItem: ->
|
||||
@getActivePane()?.saveActiveItem()
|
||||
|
||||
# Public: save the active item as.
|
||||
saveActivePaneItemAs: ->
|
||||
@getActivePane()?.saveActiveItemAs()
|
||||
|
||||
# Public: Focuses the previous pane by id.
|
||||
focusPreviousPane: -> @panes.focusPreviousPane()
|
||||
focusPreviousPane: -> @model.activatePreviousPane()
|
||||
|
||||
# Public: Focuses the next pane by id.
|
||||
focusNextPane: -> @panes.focusNextPane()
|
||||
focusNextPane: -> @model.activateNextPane()
|
||||
|
||||
# Public:
|
||||
#
|
||||
# FIXME: Difference between active and focused pane?
|
||||
getFocusedPane: -> @panes.getFocusedPane()
|
||||
|
||||
# Public: Saves all of the open items within panes.
|
||||
saveAll: ->
|
||||
@panes.saveAll()
|
||||
|
||||
# Public: Fires a callback on each open {PaneView}.
|
||||
eachPane: (callback) ->
|
||||
@panes.eachPane(callback)
|
||||
@ -350,20 +239,6 @@ class WorkspaceView extends View
|
||||
|
||||
# Private: Destroys everything.
|
||||
remove: ->
|
||||
@model.destroy()
|
||||
editorView.remove() for editorView in @getEditorViews()
|
||||
super
|
||||
|
||||
# Private: Adds the destroyed item's uri to the list of items to reopen.
|
||||
onPaneItemDestroyed: (e, item) =>
|
||||
if uri = item.getUri?()
|
||||
@destroyedItemUris.push(uri)
|
||||
|
||||
# Public: Reopens the last-closed item uri if it hasn't already been reopened.
|
||||
reopenItemSync: ->
|
||||
if uri = @destroyedItemUris.pop()
|
||||
@openSync(uri)
|
||||
|
||||
# Private: Removes the item's uri from the list of potential items to reopen.
|
||||
itemOpened: (item) ->
|
||||
if uri = item.getUri?()
|
||||
_.remove(@destroyedItemUris, uri)
|
||||
|
143
src/workspace.coffee
Normal file
143
src/workspace.coffee
Normal file
@ -0,0 +1,143 @@
|
||||
{remove, last} = require 'underscore-plus'
|
||||
{Model} = require 'theorist'
|
||||
Q = require 'q'
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
PaneContainer = require './pane-container'
|
||||
Pane = require './pane'
|
||||
|
||||
# Public: Represents the view state of the entire window, including the panes at
|
||||
# the center and panels around the periphery. You can access the singleton
|
||||
# instance via `atom.workspace`.
|
||||
module.exports =
|
||||
class Workspace extends Model
|
||||
atom.deserializers.add(this)
|
||||
Serializable.includeInto(this)
|
||||
|
||||
@delegatesProperty 'activePane', 'activePaneItem', toProperty: 'paneContainer'
|
||||
@delegatesMethod 'getPanes', 'saveAll', 'activateNextPane', 'activatePreviousPane',
|
||||
toProperty: 'paneContainer'
|
||||
|
||||
@properties
|
||||
paneContainer: -> new PaneContainer
|
||||
fullScreen: false
|
||||
destroyedItemUris: -> []
|
||||
|
||||
# Private:
|
||||
constructor: ->
|
||||
super
|
||||
@subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed
|
||||
|
||||
# Private: Called by the Serializable mixin during deserialization
|
||||
deserializeParams: (params) ->
|
||||
params.paneContainer = PaneContainer.deserialize(params.paneContainer)
|
||||
params
|
||||
|
||||
# Private: Called by the Serializable mixin during serialization.
|
||||
serializeParams: ->
|
||||
paneContainer: @paneContainer.serialize()
|
||||
fullScreen: atom.isFullScreen()
|
||||
|
||||
# Public: Asynchronously opens a given a filepath in Atom.
|
||||
#
|
||||
# * filePath: A file path
|
||||
# * options
|
||||
# + initialLine: The buffer line number to open to.
|
||||
#
|
||||
# Returns a promise that resolves to the {Editor} for the file URI.
|
||||
open: (filePath, options={}) ->
|
||||
changeFocus = options.changeFocus ? true
|
||||
filePath = atom.project.resolve(filePath)
|
||||
initialLine = options.initialLine
|
||||
activePane = @activePane
|
||||
|
||||
editor = activePane.itemForUri(atom.project.relativize(filePath)) if activePane and filePath
|
||||
promise = atom.project.open(filePath, {initialLine}) if not editor
|
||||
|
||||
Q(editor ? promise)
|
||||
.then (editor) =>
|
||||
if not activePane
|
||||
activePane = new Pane(items: [editor])
|
||||
@paneContainer.root = activePane
|
||||
|
||||
@itemOpened(editor)
|
||||
activePane.activateItem(editor)
|
||||
activePane.activate() if changeFocus
|
||||
@emit "uri-opened"
|
||||
editor
|
||||
.catch (error) ->
|
||||
console.error(error.stack ? error)
|
||||
|
||||
# Private: Only used in specs
|
||||
openSync: (uri, {changeFocus, initialLine, pane, split}={}) ->
|
||||
changeFocus ?= true
|
||||
pane ?= @activePane
|
||||
uri = atom.project.relativize(uri)
|
||||
|
||||
if pane
|
||||
if uri
|
||||
paneItem = pane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine})
|
||||
else
|
||||
paneItem = atom.project.openSync()
|
||||
|
||||
if split == 'right'
|
||||
panes = @getPanes()
|
||||
if panes.length == 1
|
||||
pane = panes[0].splitRight()
|
||||
else
|
||||
pane = last(panes)
|
||||
else if split == 'left'
|
||||
pane = @getPanes()[0]
|
||||
|
||||
pane.activateItem(paneItem)
|
||||
else
|
||||
paneItem = atom.project.openSync(uri, {initialLine})
|
||||
pane = new Pane(items: [paneItem])
|
||||
@paneContainer.root = pane
|
||||
|
||||
@itemOpened(paneItem)
|
||||
|
||||
pane.activate() if changeFocus
|
||||
paneItem
|
||||
|
||||
# Public: Synchronously open an editor for the given URI or activate an existing
|
||||
# editor in any pane if one already exists.
|
||||
openSingletonSync: (uri, {changeFocus, initialLine, split}={}) ->
|
||||
changeFocus ?= true
|
||||
uri = atom.project.relativize(uri)
|
||||
pane = @paneContainer.paneForUri(uri)
|
||||
|
||||
if pane
|
||||
paneItem = pane.itemForUri(uri)
|
||||
pane.activateItem(paneItem)
|
||||
pane.activate() if changeFocus
|
||||
paneItem
|
||||
else
|
||||
@openSync(uri, {changeFocus, initialLine, split})
|
||||
|
||||
# Public: Reopens the last-closed item uri if it hasn't already been reopened.
|
||||
reopenItemSync: ->
|
||||
if uri = @destroyedItemUris.pop()
|
||||
@openSync(uri)
|
||||
|
||||
# Public: save the active item.
|
||||
saveActivePaneItem: ->
|
||||
@activePane?.saveActiveItem()
|
||||
|
||||
# Public: save the active item as.
|
||||
saveActivePaneItemAs: ->
|
||||
@activePane?.saveActiveItemAs()
|
||||
|
||||
# Public: destroy/close the active item.
|
||||
destroyActivePaneItem: ->
|
||||
@activePane?.destroyActiveItem()
|
||||
|
||||
# Private: Removes the item's uri from the list of potential items to reopen.
|
||||
itemOpened: (item) ->
|
||||
if uri = item.getUri?()
|
||||
remove(@destroyedItemUris, uri)
|
||||
|
||||
# Private: Adds the destroyed item's uri to the list of items to reopen.
|
||||
onPaneItemDestroyed: (item) =>
|
||||
if uri = item.getUri?()
|
||||
@destroyedItemUris.push(uri)
|
2
vendor/apm
vendored
2
vendor/apm
vendored
@ -1 +1 @@
|
||||
Subproject commit 35edbb07fb4abba49dd97d12a1ad8c4adb71625f
|
||||
Subproject commit b843d54a8ac495231efd680ab3648dfd9247896c
|
Loading…
Reference in New Issue
Block a user