mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-09-20 23:48:05 +03:00
Merge branch 'master' into cj-deleted-files-not-modified
Conflicts: src/pane.coffee
This commit is contained in:
commit
2d5b04579f
@ -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) ->`
|
||||
|
@ -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
39
docs/setting-up-travis.md
Normal 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
|
18
package.json
18
package.json
@ -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": {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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", ->
|
||||
|
@ -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'))
|
||||
|
@ -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')
|
||||
|
@ -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 ->
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
2
vendor/apm
vendored
@ -1 +1 @@
|
||||
Subproject commit 007cf2643366f4e4aa5e38083bc69da9dc363098
|
||||
Subproject commit 77bc20a81497f2ac1944619fc85098293b7de0f5
|
Loading…
Reference in New Issue
Block a user