Merge branch 'master' into cj-deleted-files-not-modified

Conflicts:
	src/pane.coffee
This commit is contained in:
probablycorey 2013-12-12 09:57:28 -08:00
commit 2d5b04579f
24 changed files with 308 additions and 249 deletions

View File

@ -27,8 +27,9 @@ in the proper package's repository.
## Pull Requests
* Include screenshots and animated GIFs whenever possible.
* Follow the [JavaScript](https://github.com/styleguide/javascript) and
[CSS](https://github.com/styleguide/css) styleguides
* Follow the [CoffeeScript](#coffeescript-styleguide),
[JavaScript](https://github.com/styleguide/javascript),
and [CSS](https://github.com/styleguide/css) styleguides
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/)
specs
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
@ -55,3 +56,8 @@ in the proper package's repository.
* :racehorse: when improving performance
* :non-potable_water: when plugging memory leaks
* :memo: when writing docs
## CoffeeScript Styleguide
* Set parameter defaults without spaces around the equal sign
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`

View File

@ -1,4 +1,4 @@
# Atom — The hackable, ~~collaborative~~ editor of tomorrow!
# Atom — The hackable, ~~collaborative~~ editor
![Atom](http://i.imgur.com/OrTvUAD.png)

39
docs/setting-up-travis.md Normal file
View File

@ -0,0 +1,39 @@
# Setting up Travis CI
Packages under the [atom org][atom-org] should use [Travis CI][travis-ci] for
builds.
Currently we have a [Travis Pro][travis-pro] account since the repositories
are private. This process will be simpler and have fewer steps once the
package repos are made public.
Each package builds using the [build-package][build-package] script, any
changes to the build should be made in that script and will affect all
package builds immediately.
Each package builds against the latest successful build of atom@master. The
master builds are stored in the [atom-master-builds][atom-master-builds] repo
as releases named by the SHA-1 built.
## Configuring a package
* Run `cd ~/github/my-package` to navigate to the package repo locally
* Run `apm test` to verify that the package currently builds via [apm][apm]
* Add the package repo to the [Travis CI team][travis-ci-team]
* Run `gem install travis` to install the [travis gem][travis-gem]
* Run `travis login --pro` and log in using the [atom-build][atom-build] user
and the password from the *Shared-Developers* folder in LastPass
* Run `apm ci` to add a `.travis.yml` file to the repo and to configure Travis
* Log into [Travis][travis-ci] as the `atom-build` user and you should now see
the package listed and building
[apm]: https://github.com/atom/apm
[build-package]: https://github.com/atom/apm/blob/master/script/build-package
[atom-build]: https://github.com/atom-build
[atom-master-builds]: https://github.com/atom/atom-master-builds/releases
[atom-org]: https://github.com/atom
[travis-ci]: https://magnum.travis-ci.com
[travis-ci-team]: https://github.com/organizations/atom/teams/596636
[travis-gem]: https://rubygems.org/gems/travis
[travis-pro]: http://about.travis-ci.org/docs/user/travis-pro

View File

@ -25,7 +25,7 @@
"coffeestack": "0.6.0",
"emissary": "0.19.0",
"first-mate": "0.5.0",
"fs-plus": "0.10.0",
"fs-plus": "0.11.0",
"fuzzaldrin": "0.1.0",
"git-utils": "0.29.0",
"guid": "0.0.10",
@ -43,9 +43,9 @@
"season": "0.14.0",
"semver": "1.1.4",
"space-pen": "2.0.1",
"telepath": "0.65.0",
"telepath": "0.68.0",
"temp": "0.5.0",
"underscore-plus": "0.4.0"
"underscore-plus": "0.5.0"
},
"devDependencies": {
"biscotto": "0.0.17",
@ -74,9 +74,9 @@
},
"packageDependencies": {
"atom-dark-syntax": "0.8.0",
"atom-dark-ui": "0.14.0",
"atom-dark-ui": "0.17.0",
"atom-light-syntax": "0.9.0",
"atom-light-ui": "0.14.0",
"atom-light-ui": "0.16.0",
"base16-tomorrow-dark-theme": "0.7.0",
"solarized-dark-syntax": "0.5.0",
"archive-view": "0.16.0",
@ -102,7 +102,7 @@
"keybinding-resolver": "0.6.0",
"link": "0.11.0",
"markdown-preview": "0.22.0",
"metrics": "0.14.0",
"metrics": "0.17.0",
"package-generator": "0.23.0",
"release-notes": "0.15.0",
"settings-view": "0.50.0",
@ -115,7 +115,7 @@
"terminal": "0.23.0",
"timecop": "0.11.0",
"to-the-hubs": "0.15.0",
"tree-view": "0.43.0",
"tree-view": "0.44.0",
"visual-bell": "0.6.0",
"welcome": "0.3.0",
"whitespace": "0.10.0",
@ -130,7 +130,7 @@
"language-html": "0.2.0",
"language-hyperlink": "0.3.0",
"language-java": "0.2.0",
"language-javascript": "0.3.0",
"language-javascript": "0.4.0",
"language-json": "0.2.0",
"language-less": "0.1.0",
"language-make": "0.1.0",
@ -153,7 +153,7 @@
"language-toml": "0.7.0",
"language-xml": "0.2.0",
"language-yaml": "0.1.0",
"grunt-download-atom-shell": "0.1.3"
"grunt-download-atom-shell": "0.2.0"
},
"private": true,
"scripts": {

View File

@ -22,15 +22,16 @@ function executeCommands(commands, done, index) {
done(null);
}
var apmFlags = process.env.JANKY_SHA1 ? '--no-color' : '';
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
var commands = [
'git submodule --quiet sync',
'git submodule --quiet update --recursive --init',
{command: 'npm install --silent .', options: {cwd: path.resolve(__dirname, '..', 'vendor', 'apm'), ignoreStdout: true}},
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
{command: 'npm install --quiet .', options: {cwd: path.resolve(__dirname, '..', 'vendor', 'apm'), ignoreStdout: true}},
{command: 'npm install --quiet vendor/apm', options: {ignoreStdout: true}},
echoNewLine,
'node node_modules/atom-package-manager/bin/apm clean',
'node node_modules/atom-package-manager/bin/apm install --silent',
'node node_modules/atom-package-manager/bin/apm clean ' + apmFlags,
'node node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
];
process.chdir(path.dirname(__dirname));

View File

@ -5,8 +5,7 @@ var path = require('path');
process.chdir(path.dirname(__dirname));
cp.safeExec('node script/bootstrap', function() {
// node node_modules/grunt-cli/bin/grunt "$@"
var gruntPath = path.join('node_modules', 'grunt-cli', 'bin', 'grunt');
var args = [gruntPath].concat(process.argv.slice(2));
cp.safeSpawn(process.execPath, args, process.exit);
// node_modules/.bin/grunt "$@"
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
cp.safeSpawn(gruntPath, process.argv.slice(2), process.exit);
});

View File

@ -29,10 +29,11 @@ cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
if (error)
process.exit(1);
var async = require('async');
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
async.series([
require('rimraf').bind(global, path.join(homeDir, '.atom')),
cp.safeExec.bind(global, 'git clean -dff'),
cp.safeExec.bind(global, 'node node_modules/grunt-cli/bin/grunt ci --stack --no-color'),
cp.safeExec.bind(global, gruntPath + ' ci --stack --no-color'),
cp.safeExec.bind(global, 'node_modules/.bin/coffee script/upload-release')
], function(error) {
process.exit(error ? 1 : 0);

View File

@ -5,5 +5,6 @@ var path = require('path');
process.chdir(path.dirname(__dirname));
safeExec('node script/bootstrap', function() {
safeExec('node node_modules/grunt-cli/bin/grunt ci --stack --no-color', process.exit);
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
safeExec(gruntPath + ' ci --stack --no-color', process.exit);
});

View File

@ -5,52 +5,68 @@ return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
child_process = require 'child_process'
path = require 'path'
_ = require 'underscore-plus'
fs = require 'fs-plus'
GitHub = require 'github-releases'
request = require 'request'
assetPath = '/tmp/atom-build/atom-mac.zip'
maxReleases = 10
assetName = 'atom-mac.zip'
assetPath = "/tmp/atom-build/#{assetName}"
commitSha = process.env.JANKY_SHA1
token = process.env.ATOM_ACCESS_TOKEN
defaultHeaders =
Authorization: "token #{token}"
'User-Agent': 'Atom'
zipApp = (callback) ->
fs.removeSync(assetPath)
options = {cwd: path.dirname(assetPath), maxBuffer: Infinity}
child_process.exec "zip -r --symlinks #{path.basename(assetPath)} Atom.app", options, (error, stdout, stderr) ->
child_process.exec "zip -r --symlinks #{assetName} Atom.app", options, (error, stdout, stderr) ->
if error?
console.error('Zipping Atom.app failed', error, stderr)
process.exit(1)
else
callback()
getDraftRelease = (callback) ->
github = new GitHub({repo: 'atom/atom', token})
github.getReleases (error, releases=[]) ->
if error?
console.error('Fetching releases failed', error.message, error.stack)
getRelease = (callback) ->
options =
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
method: 'GET'
headers: defaultHeaders
json: true
request options, (error, response, releases=[]) ->
if error? or response.statusCode isnt 200
console.error('Fetching releases failed', error, releases)
process.exit(1)
else
if releases.length > maxReleases
deleteRelease(release) for release in releases[maxReleases..]
if releases.length is 0
console.error('No releases found in atom/atom repo')
process.exit(1)
for release in releases when release.name is commitSha
callback(release)
return
callback()
for release in releases when release.draft
callback(release)
return
console.error('No draft release found in atom/atom repo')
process.exit(1)
deleteRelease = (release) ->
options =
uri: release.url
method: 'DELETE'
headers: defaultHeaders
json: true
request options, (error, response, body='') ->
if error? or response.statusCode isnt 204
console.error('Deleting release failed', error, body)
deleteExistingAsset = (release, callback) ->
for asset in release.assets when asset.name is path.basename(assetPath)
for asset in release.assets when asset.name is assetName
options =
uri: asset.url
method: 'DELETE'
headers:
Authorization: "token #{token}"
'User-Agent': 'Atom'
headers: defaultHeaders
request options, (error, response, body='') ->
if error? or response.statusCode >= 400
if error? or response.statusCode isnt 204
console.error('Deleting existing release asset failed', error, body)
process.exit(1)
else
@ -60,24 +76,62 @@ deleteExistingAsset = (release, callback) ->
callback()
uploadAsset = (release) ->
createRelease = (callback) ->
getRelease (release) ->
if release?
deleteExistingAsset release, ->
callback(release)
return
options =
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
method: 'POST'
headers: defaultHeaders
json:
tag_name: "v#{commitSha}"
target_commitish: 'master'
name: commitSha
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commit/#{commitSha})"
draft: true
prerelease: true
request options, (error, response, release={}) ->
if error? or response.statusCode isnt 201
console.error('Creating release failed', error, release)
process.exit(1)
else
callback(release)
uploadAsset = (release, callback) ->
options =
uri: "https://uploads.github.com/repos/atom/atom/releases/#{release.id}/assets?name=#{path.basename(assetPath)}"
uri: "https://uploads.github.com/repos/atom/atom-master-builds/releases/#{release.id}/assets?name=#{assetName}"
method: 'POST'
headers:
Authorization: "token #{token}"
headers: _.extend({
'Content-Type': 'application/zip'
'Content-Length': fs.getSizeSync(assetPath)
'User-Agent': 'Atom'
}, defaultHeaders)
assetRequest = request options, (error, response, body='') ->
if error? or response.statusCode >= 400
console.error('Upload release asset failed', error, body)
process.exit(1)
else
callback(release)
fs.createReadStream(assetPath).pipe(assetRequest)
getDraftRelease (release) ->
console.log("Uploading #{path.basename(assetPath)} asset to #{release.tag_name} (#{release.name}) release")
publishRelease = (release) ->
options =
uri: release.url
method: 'POST'
headers: defaultHeaders
json:
draft: false
request options, (error, response, body={}) ->
if error? or response.statusCode isnt 200
console.error('Creating release failed', error, body)
process.exit(1)
createRelease (release) ->
zipApp ->
deleteExistingAsset release, ->
uploadAsset(release)
uploadAsset release, ->
publishRelease release

View File

@ -438,3 +438,11 @@ describe "the `atom` global", ->
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.themes')).not.toContain packageName
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
describe ".isReleasedVersion()", ->
it "returns false if the version is a SHA and true otherwise", ->
version = '0.1.0'
spyOn(atom, 'getVersion').andCallFake -> version
expect(atom.isReleasedVersion()).toBe true
version = '36b5518'
expect(atom.isReleasedVersion()).toBe false

View File

@ -21,20 +21,24 @@ describe "Editor", ->
buffer = editor.buffer
lineLengths = buffer.getLines().map (line) -> line.length
describe "@deserialize(state)", ->
describe "when the editor is deserialized", ->
it "restores selections and folds based on markers in the buffer", ->
editor.setSelectedBufferRange([[1, 2], [3, 4]])
editor.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true)
editor.foldBufferRow(4)
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
editor2 = atom.deserializers.deserialize(editor.serialize())
# Simulate serialization with replicate
editor2 = editor.replicate()
# FIXME: The created hook is called manually on deserialization because globals aren't ready otherwise
editor2.created()
expect(editor2.id).toBe editor.id
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
expect(editor2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]]
expect(editor2.getSelection(1).isReversed()).toBeTruthy()
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
editor2.destroy()
describe ".copy()", ->
it "returns a different edit session with the same initial state", ->

View File

@ -88,73 +88,6 @@ describe "PaneContainer", ->
pane4.splitDown()
expect(panes).toEqual []
describe ".reopenItem()", ->
describe "when there is an active pane", ->
it "reconstructs and shows the last-closed pane item", ->
expect(container.getActivePane()).toBe pane3
item3 = pane3.activeItem
item4 = new TestView('4')
pane3.showItem(item4)
pane3.destroyItem(item3)
pane3.destroyItem(item4)
expect(container.getActivePane()).toBe pane1
expect(container.reopenItem()).toBeTruthy()
expect(pane1.activeItem).toEqual item4
expect(container.reopenItem()).toBeTruthy()
expect(pane1.activeItem).toEqual item3
expect(container.reopenItem()).toBeFalsy()
expect(pane1.activeItem).toEqual item3
describe "when the last-closed pane item is an edit session", ->
it "reopens the edit session (regression)", ->
editor = atom.project.openSync('sample.js')
pane3.showItem(editor)
pane3.destroyItem(editor)
expect(container.reopenItem()).toBeTruthy()
expect(pane3.activeItem.getPath()).toBe editor.getPath()
expect(container.reopenItem()).toBeFalsy()
describe "when there is no active pane", ->
it "attaches a new pane with the reconstructed last pane item and focuses it", ->
container.attachToDom()
pane1.remove()
pane2.remove()
item3 = pane3.activeItem
pane3.destroyItem(item3)
expect(container.getActivePane()).toBeUndefined()
container.reopenItem()
expect(container.getActivePane().activeItem).toEqual item3
expect(container.getActivePane().activeView).toMatchSelector ':focus'
it "does not reopen an item that is already open", ->
item3 = pane3.activeItem
item4 = new TestView('4')
pane3.showItem(item4)
pane3.destroyItem(item3)
pane3.destroyItem(item4)
expect(container.getActivePane()).toBe pane1
pane1.showItem(new TestView('4'))
expect(container.reopenItem()).toBeTruthy()
expect(_.pluck(pane1.getItems(), 'name')).toEqual ['1', '4', '3']
expect(pane1.activeItem).toEqual item3
expect(container.reopenItem()).toBeFalsy()
expect(pane1.activeItem).toEqual item3
pane1.destroyItem(item3)
container.setRoot(new Pane(item3))
expect(container.reopenItem()).toBeFalsy()
expect(container.getActivePane().getItems().length).toBe 1
expect(container.getActivePaneItem()).toEqual item3
describe ".saveAll()", ->
it "saves all open pane items", ->
pane1.showItem(new TestView('4'))

View File

@ -59,22 +59,6 @@ describe "Project", ->
editor.saveAs(tempFile)
expect(atom.project.getPath()).toBe path.dirname(tempFile)
describe "when an edit session is deserialized", ->
it "emits an 'editor-created' event and stores the edit session", ->
handler = jasmine.createSpy('editorCreatedHandler')
atom.project.on 'editor-created', handler
editor1 = atom.project.openSync("a")
expect(handler.callCount).toBe 1
expect(atom.project.getEditors().length).toBe 1
expect(atom.project.getEditors()[0]).toBe editor1
editor2 = atom.deserializers.deserialize(editor1.serialize())
expect(handler.callCount).toBe 2
expect(atom.project.getEditors().length).toBe 2
expect(atom.project.getEditors()[0]).toBe editor1
expect(atom.project.getEditors()[1]).toBe editor2
describe "when an edit session is copied", ->
it "emits an 'editor-created' event and stores the edit session", ->
handler = jasmine.createSpy('editorCreatedHandler')

View File

@ -5,7 +5,7 @@ describe "Selection", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
editor = new Editor(buffer: buffer, tabLength: 2)
editor = atom.create(new Editor(buffer: buffer, tabLength: 2))
selection = editor.getSelection()
afterEach ->

View File

@ -522,3 +522,36 @@ describe "WorkspaceView", ->
subscription.off()
atom.workspaceView.getActiveView().splitRight()
expect(count).toBe 2
describe ".reopenItemSync()", ->
it "opens the uri associated with the last closed pane that isn't currently open", ->
workspace = atom.workspaceView
pane = workspace.getActivePane()
workspace.openSync('b')
workspace.openSync('file1')
workspace.openSync()
# does not reopen items with no uri
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
pane.destroyActiveItem()
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
# destroy all items
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
pane.destroyActiveItem()
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
pane.destroyActiveItem()
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
pane.destroyActiveItem()
# reopens items with uris
expect(workspace.getActivePaneItem()).toBeUndefined()
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
# does not reopen items that are already open
workspace.openSync('b')
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
workspace.reopenItemSync()
expect(workspace.getActivePaneItem().getUri()).toBe 'a'

View File

@ -9,7 +9,8 @@ dialog = remote.require 'dialog'
app = remote.require 'app'
_ = require 'underscore-plus'
{Document} = require 'telepath'
telepath = require 'telepath'
{Document} = telepath
fs = require 'fs-plus'
{Subscriber} = require 'emissary'
@ -53,6 +54,8 @@ class Atom
{devMode, resourcePath} = atom.getLoadSettings()
configDirPath = @getConfigDirPath()
telepath.devMode = not @isReleasedVersion()
Config = require './config'
Keymap = require './keymap'
PackageManager = require './package-manager'
@ -361,6 +364,10 @@ class Atom
getVersion: ->
app.getVersion()
# Public: Determine whether the current version is an official release.
isReleasedVersion: ->
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
getGitHubAuthTokenName: ->
'Atom GitHub API Token'
@ -427,7 +434,13 @@ class Atom
serializedWindowState = @loadSerializedWindowState()
doc = Document.deserialize(serializedWindowState) if serializedWindowState?
doc ?= Document.create()
doc.registerModelClasses(require('./text-buffer'), require('./project'), require('./tokenized-buffer'), require('./display-buffer'))
doc.registerModelClasses(
require('./text-buffer'),
require('./project'),
require('./tokenized-buffer'),
require('./display-buffer'),
require('./editor')
)
# TODO: Remove this when everything is using telepath models
if @site?
@site.setRootDocument(doc)

View File

@ -1,4 +1,4 @@
{Document} = require 'telepath'
{Document, Model} = require 'telepath'
# Public: Manages the deserializers used for serialized state
#
@ -25,6 +25,8 @@ class DeserializerManager
deserialize: (state, params) ->
return unless state?
return state if state instanceof Model
if deserializer = @get(state)
stateVersion = state.get?('version') ? state.version
return if deserializer.version? and deserializer.version isnt stateVersion

View File

@ -105,12 +105,12 @@ class EditorView extends View
if editor?
@edit(editor)
else if @mini
@edit(new Editor
@edit(atom.create(new Editor
buffer: atom.create(new TextBuffer)
softWrap: false
tabLength: 2
softTabs: true
)
))
else
throw new Error("Must supply an Editor or mini: true")

View File

@ -1,13 +1,11 @@
_ = require 'underscore-plus'
path = require 'path'
telepath = require 'telepath'
guid = require 'guid'
{Point, Range} = telepath
{Model, Point, Range} = require 'telepath'
LanguageMode = require './language-mode'
DisplayBuffer = require './display-buffer'
Cursor = require './cursor'
Selection = require './selection'
{Emitter, Subscriber} = require 'emissary'
TextMateScopeSelector = require('first-mate').ScopeSelector
# Public: The core model of Atom.
@ -36,82 +34,70 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
# FIXME: Describe how there are both local and remote cursors and selections and
# why that is.
module.exports =
class Editor
Emitter.includeInto(this)
Subscriber.includeInto(this)
class Editor extends Model
@acceptsDocuments: true
@properties
displayBuffer: null
softTabs: null
scrollTop: 0
scrollLeft: 0
atom.deserializers.add(this)
@version: 6
@deserialize: (state) ->
new Editor(state)
id: null
deserializing: false
callDisplayBufferCreatedHook: false
buffer: null
languageMode: null
displayBuffer: null
cursors: null
remoteCursors: null
selections: null
remoteSelections: null
suppressSelectionMerging: false
# Private:
constructor: (optionsOrState) ->
constructor: ->
super
@deserializing = @state?
created: ->
if @deserializing
@deserializing = false
@callDisplayBufferCreatedHook = true
return
@cursors = []
@remoteCursors = []
@selections = []
@remoteSelections = []
if optionsOrState instanceof telepath.Document
@state = optionsOrState
@id = @state.get('id')
displayBuffer = @state.get('displayBuffer')
displayBuffer.created()
@setBuffer(displayBuffer.buffer)
@setDisplayBuffer(displayBuffer)
for marker in @findMarkers(@getSelectionMarkerAttributes())
marker.setAttributes(preserveFolds: true)
@addSelection(marker)
@setScrollTop(@state.get('scrollTop'))
@setScrollLeft(@state.get('scrollLeft'))
registerEditor = true
else
{buffer, displayBuffer, tabLength, softTabs, softWrap, suppressCursorCreation, initialLine} = optionsOrState
@id = guid.create().toString()
displayBuffer ?= atom.create(new DisplayBuffer({buffer, tabLength, softWrap}))
@state = atom.site.createDocument
deserializer: @constructor.name
version: @constructor.version
id: @id
displayBuffer: displayBuffer
softTabs: buffer.usesSoftTabs() ? softTabs ? atom.config.get('editor.softTabs') ? true
scrollTop: 0
scrollLeft: 0
@setBuffer(buffer)
@setDisplayBuffer(displayBuffer)
if @getCursors().length is 0 and not suppressCursorCreation
if initialLine
position = [initialLine, 0]
unless @displayBuffer?
@displayBuffer = new DisplayBuffer({@buffer, @tabLength, @softWrap})
@softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
@displayBuffer.created() if @callDisplayBufferCreatedHook
@buffer = @displayBuffer.buffer
for marker in @findMarkers(@getSelectionMarkerAttributes())
marker.setAttributes(preserveFolds: true)
@addSelection(marker)
@subscribeToBuffer()
@subscribeToDisplayBuffer()
if @getCursors().length is 0 and not @suppressCursorCreation
if @initialLine
position = [@initialLine, 0]
else
position = _.last(@getRemoteCursors())?.getBufferPosition() ? [0, 0]
@addCursorAtBufferPosition(position)
@languageMode = new LanguageMode(this, @buffer.getExtension())
@subscribe @state, 'changed', ({newValues}) =>
for key, newValue of newValues
switch key
when 'scrollTop'
@emit 'scroll-top-changed', newValue
when 'scrollLeft'
@emit 'scroll-left-changed', newValue
atom.project.addEditor(this) if registerEditor
@subscribe @$scrollTop, 'value', (scrollTop) => @emit 'scroll-top-changed', scrollTop
@subscribe @$scrollLeft, 'value', (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
# Deprecated: The goal is a world where we don't call serialize explicitly
serialize: -> this
# Private:
setBuffer: (@buffer) ->
subscribeToBuffer: ->
@buffer.retain()
@subscribe @buffer, "path-changed", =>
unless atom.project.getPath()?
@ -125,7 +111,7 @@ class Editor
@preserveCursorPositionOnBufferReload()
# Private:
setDisplayBuffer: (@displayBuffer) ->
subscribeToDisplayBuffer: ->
@subscribe @displayBuffer, 'marker-created', @handleMarkerCreated
@subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e
@subscribe @displayBuffer, "markers-updated", => @mergeIntersectingSelections()
@ -149,18 +135,12 @@ class Editor
@emit 'destroyed'
@off()
# Private:
serialize: -> @state.clone()
# Private:
getState: -> @state
# Private: Creates an {Editor} with the same initial state
copy: ->
tabLength = @getTabLength()
displayBuffer = @displayBuffer.copy()
softTabs = @getSoftTabs()
newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true})
newEditor = @create(new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true}))
newEditor.setScrollTop(@getScrollTop())
newEditor.setScrollLeft(@getScrollLeft())
for marker in @findMarkers(editorId: @id)
@ -212,17 +192,17 @@ class Editor
# Public: Controls visiblity based on the given Boolean.
setVisible: (visible) -> @displayBuffer.setVisible(visible)
# Public: FIXME: I don't understand this.
setScrollTop: (scrollTop) -> @state.set('scrollTop', scrollTop)
# Deprecated: Use the ::scrollTop property directly
setScrollTop: (@scrollTop) -> @scrollTop
# Public: Returns the current `scrollTop` value
getScrollTop: -> @state.get('scrollTop') ? 0
# Deprecated: Use the ::scrollTop property directly
getScrollTop: -> @scrollTop
# Public: FIXME: I don't understand this.
setScrollLeft: (scrollLeft) -> @state.set('scrollLeft', scrollLeft)
# Deprecated: Use the ::scrollLeft property directly
setScrollLeft: (@scrollLeft) -> @scrollLeft
# Public: Returns the current `scrollLeft` value
getScrollLeft: -> @state.get('scrollLeft')
# Deprecated: Use the ::scrollLeft property directly
getScrollLeft: -> @scrollLeft
# Set the number of characters that can be displayed horizontally in the
# editor that contains this edit session.
@ -234,12 +214,11 @@ class Editor
# Public: Sets the column at which columsn will soft wrap
getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
# Public: Returns whether soft tabs are enabled or not.
getSoftTabs: -> @state.get('softTabs')
# Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled.
getSoftTabs: -> @softTabs
# Public: Controls whether soft tabs are enabled or not.
setSoftTabs: (softTabs) ->
@state.set('softTabs', softTabs)
# Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled.
setSoftTabs: (@softTabs) -> @softTabs
# Public: Returns whether soft wrap is enabled or not.
getSoftWrap: -> @displayBuffer.getSoftWrap()

View File

@ -19,8 +19,6 @@ class PaneContainer extends View
@div class: 'panes'
initialize: (state) ->
@destroyedItemStates = []
if state instanceof telepath.Document
@state = state
@setRoot(atom.deserializers.deserialize(@state.get('root')))
@ -86,25 +84,8 @@ class PaneContainer extends View
nextIndex = (currentIndex + 1) % panes.length
panes[nextIndex].makeActive()
reopenItem: ->
if lastItemState = @destroyedItemStates.pop()
if activePane = @getActivePane()
activePane.showItem(atom.deserializers.deserialize(lastItemState))
true
else
newPane = new Pane(atom.deserializers.deserialize(lastItemState))
@setRoot(newPane)
newPane.focus()
itemDestroyed: (item) ->
if state = item.serialize?()
state.uri ?= item.getUri?()
@destroyedItemStates.push(state)
itemAdded: (item) ->
itemUri = item.getUri?()
@destroyedItemStates = @destroyedItemStates.filter (itemState) ->
itemState.uri isnt itemUri
@trigger 'item-destroyed', item
getRoot: ->
@children().first().view()
@ -113,7 +94,6 @@ class PaneContainer extends View
@empty()
if root?
@append(root)
@itemAdded(root.activeItem) if root.activeItem?
root.makeActive?()
@state.set(root: root?.getState())

View File

@ -34,8 +34,10 @@ class Pane extends View
@items = []
if args[0] instanceof telepath.Document
@state = args[0]
_.compact @state.get('items').each (item) =>
@addItem(item, @items.length) if item = atom.deserializers.deserialize(item)
@state.get('items').each (item) =>
if item = atom.deserializers.deserialize(item)
item?.created?()
@addItem(item, @items.length)
else
items = args
@state = atom.site.createDocument
@ -187,7 +189,6 @@ class Pane extends View
@state.get('items').splice(index, 0, item.getState?() ? item.serialize()) if options.updateState ? true
@items.splice(index, 0, item)
@getContainer()?.itemAdded(item)
@trigger 'pane:item-added', [item, index]
if item.on
@subscribe item, 'destroyed', => @destroyItem(item)

View File

@ -334,7 +334,7 @@ class Project extends telepath.Model
# Private:
buildEditorForBuffer: (buffer, editorOptions) ->
editor = new Editor(_.extend({buffer}, editorOptions))
editor = @create(new Editor(_.extend({buffer}, editorOptions)))
@addEditor(editor)
editor

View File

@ -79,6 +79,10 @@ class WorkspaceView extends View
@panes.replaceWith(panes)
@panes = panes
@destroyedItemUris = []
@subscribe @panes, 'item-destroyed', @onPaneItemDestroyed
@updateTitle()
@on 'focus', (e) => @handleFocus(e)
@ -122,8 +126,7 @@ class WorkspaceView extends View
@command 'window:toggle-auto-indent', =>
atom.config.toggle("editor.autoIndent")
@command 'pane:reopen-closed-item', =>
@panes.reopenItem()
@command 'pane:reopen-closed-item', => @reopenItemSync()
# Private:
serialize: ->
@ -180,6 +183,7 @@ class WorkspaceView extends View
activePane = new Pane(editor)
@panes.setRoot(activePane)
@itemOpened(editor)
activePane.showItem(editor)
activePane.focus() if changeFocus
@trigger "uri-opened"
@ -214,6 +218,8 @@ class WorkspaceView extends View
pane = new Pane(paneItem)
@panes.setRoot(pane)
@itemOpened(paneItem)
pane.focus() if changeFocus
paneItem
@ -315,3 +321,18 @@ class WorkspaceView extends View
editorView.remove() for editorView in @getEditorViews()
atom.project?.destroy()
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)

2
vendor/apm vendored

@ -1 +1 @@
Subproject commit 007cf2643366f4e4aa5e38083bc69da9dc363098
Subproject commit 77bc20a81497f2ac1944619fc85098293b7de0f5