mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-20 23:48:05 +03:00
Merge remote-tracking branch 'origin/master' into cj-remove-editor-view-delegate-methods
Conflicts: package.json
This commit is contained in:
commit
ac07cf3d9a
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'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -147,8 +147,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'
|
||||
@ -221,7 +223,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'])
|
||||
|
@ -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
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.45.0",
|
||||
"version": "0.46.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -48,11 +48,12 @@
|
||||
"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",
|
||||
"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",
|
||||
@ -68,7 +69,7 @@
|
||||
"autosave": "0.10.0",
|
||||
"background-tips": "0.4.0",
|
||||
"bookmarks": "0.16.0",
|
||||
"bracket-matcher": "0.17.0",
|
||||
"bracket-matcher": "0.18.0",
|
||||
"command-logger": "0.8.0",
|
||||
"command-palette": "0.14.0",
|
||||
"dev-live-reload": "0.22.0",
|
||||
|
@ -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)
|
||||
|
@ -17,6 +17,8 @@ var commands = [
|
||||
killatom,
|
||||
[__dirname, '..', 'node_modules'],
|
||||
[__dirname, '..', 'build', 'node_modules'],
|
||||
[__dirname, '..', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'vendor', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'atom-shell'],
|
||||
[home, '.atom', '.node-gyp'],
|
||||
[home, '.atom', 'storage'],
|
||||
|
@ -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
|
@ -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()
|
||||
|
||||
|
@ -602,12 +602,12 @@ describe "PaneView", ->
|
||||
|
||||
describe "serialization", ->
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
newPane = new PaneView(pane.model.testSerialization())
|
||||
expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2]
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItem(editor2)
|
||||
newPane = pane.testSerialization()
|
||||
newPane = new PaneView(pane.model.testSerialization())
|
||||
expect(newPane.activeItem).toEqual editor2
|
||||
|
||||
it "does not show items that cannot be deserialized", ->
|
||||
@ -618,7 +618,7 @@ describe "PaneView", ->
|
||||
|
||||
pane.activateItem(new Unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
newPane = new PaneView(pane.model.testSerialization())
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
@ -626,13 +626,13 @@ describe "PaneView", ->
|
||||
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)')
|
||||
|
@ -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,11 +21,12 @@ 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", ->
|
||||
@ -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()
|
||||
|
@ -230,8 +230,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:
|
||||
@ -277,7 +279,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()
|
||||
|
@ -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'
|
||||
@ -54,7 +51,6 @@ class PaneView extends View
|
||||
@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 +81,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 +142,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 +154,6 @@ class PaneView extends View
|
||||
@viewsByItem.delete(item)
|
||||
|
||||
if viewToRemove?
|
||||
viewToRemove.setModel?(null)
|
||||
if destroyed
|
||||
viewToRemove.remove()
|
||||
else
|
||||
@ -181,15 +168,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 +186,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
|
||||
|
@ -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)
|
Loading…
Reference in New Issue
Block a user