Merge branch 'master' into as-cjk-soft-wrap

# Conflicts:
#	spec/display-buffer-spec.coffee
This commit is contained in:
Antonio Scandurra
2015-10-16 10:23:44 +02:00
116 changed files with 2585 additions and 2139 deletions

View File

@@ -8,7 +8,7 @@
"dependencies": {
"asar": "^0.8.0",
"async": "~0.2.9",
"donna": "1.0.10",
"donna": "^1.0.12",
"formidable": "~1.0.14",
"fs-plus": "2.x",
"github-releases": "~0.3.0",

View File

@@ -54,14 +54,14 @@ module.exports = (grunt) ->
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
args: ['--test', "--resource-path=#{resourcePath}", path.join(packagePath, 'spec')]
opts:
cwd: packagePath
env: _.extend({}, process.env, ATOM_PATH: rootDir)
else if process.platform is 'win32'
options =
cmd: process.env.comspec
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}", "--log-file=ci.log"]
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--log-file=ci.log", path.join(packagePath, 'spec')]
opts:
cwd: packagePath
env: _.extend({}, process.env, ATOM_PATH: rootDir)
@@ -95,7 +95,7 @@ module.exports = (grunt) ->
if process.platform in ['darwin', 'linux']
options =
cmd: appPath
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
args: ['--test', "--resource-path=#{resourcePath}", coreSpecsPath]
opts:
env: _.extend({}, process.env,
ATOM_INTEGRATION_TESTS_ENABLED: true
@@ -104,7 +104,7 @@ module.exports = (grunt) ->
else if process.platform is 'win32'
options =
cmd: process.env.comspec
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}", '--log-file=ci.log']
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", '--log-file=ci.log', coreSpecsPath]
opts:
env: _.extend({}, process.env,
ATOM_INTEGRATION_TESTS_ENABLED: true

View File

@@ -2,6 +2,7 @@ TextBuffer = require 'text-buffer'
{Point, Range} = TextBuffer
{File, Directory} = require 'pathwatcher'
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
module.exports =
BufferedNodeProcess: require '../src/buffered-node-process'
@@ -21,4 +22,20 @@ module.exports =
# only be exported when not running as a child node process
unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
module.exports.Task = require '../src/task'
module.exports.TextEditor = require '../src/text-editor'
TextEditor = (params) ->
atom.workspace.buildTextEditor(params)
TextEditor.prototype = require('../src/text-editor').prototype
Object.defineProperty module.exports, 'TextEditor',
enumerable: true
get: ->
Grim.deprecate """
The `TextEditor` constructor is no longer public.
To construct a text editor, use `atom.workspace.buildTextEditor()`.
To check if an object is a text editor, look for for the existence of
a public method that you're using (e.g. `::getText`).
"""
TextEditor

View File

@@ -18,7 +18,6 @@
'ctrl-d': 'core:delete'
# Atom Specific
'cmd-alt-ctrl-s': 'application:run-all-specs'
'enter': 'core:confirm'
'escape': 'core:cancel'
'up': 'core:move-up'

View File

@@ -9,7 +9,6 @@
'ctrl-alt-r': 'window:reload'
'ctrl-shift-i': 'window:toggle-dev-tools'
'ctrl-alt-p': 'window:run-package-specs'
'ctrl-alt-s': 'application:run-all-specs'
'ctrl-shift-o': 'application:open-folder'
'ctrl-alt-o': 'application:add-project-folder'
'ctrl-shift-pageup': 'pane:move-item-left'

View File

@@ -15,7 +15,6 @@
'ctrl-alt-r': 'window:reload'
'ctrl-alt-i': 'window:toggle-dev-tools'
'ctrl-alt-p': 'window:run-package-specs'
'ctrl-alt-s': 'application:run-all-specs'
'ctrl-shift-o': 'application:open-folder'
'ctrl-alt-o': 'application:add-project-folder'
'ctrl-shift-left': 'pane:move-item-left'

View File

@@ -164,7 +164,6 @@
label: 'Developer'
submenu: [
{ label: 'Open In Dev Mode…', command: 'application:open-dev' }
{ label: 'Run Atom Specs', command: 'application:run-all-specs' }
{ label: 'Run Package Specs', command: 'window:run-package-specs' }
{ label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' }
]

View File

@@ -121,7 +121,6 @@
label: 'Developer'
submenu: [
{ label: 'Open In &Dev Mode…', command: 'application:open-dev' }
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
]

View File

@@ -120,7 +120,6 @@
label: 'Developer'
submenu: [
{ label: 'Open In &Dev Mode…', command: 'application:open-dev' }
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
]

View File

@@ -15,14 +15,15 @@
"electronVersion": "0.30.7",
"dependencies": {
"async": "0.2.6",
"atom-keymap": "^6.0.0",
"atom-keymap": "^6.1.0",
"babel-core": "^5.8.21",
"bootstrap": "^3.3.4",
"clear-cut": "^2.0.1",
"coffee-script": "1.8.0",
"color": "^0.7.3",
"event-kit": "^1.3.0",
"first-mate": "^5.0.0",
"find-parent-dir": "^0.3.0",
"first-mate": "^5.1.0",
"fs-plus": "^2.8.0",
"fstream": "0.1.24",
"fuzzaldrin": "^2.1",
@@ -40,13 +41,14 @@
"pathwatcher": "^6.2",
"property-accessors": "^1.1.3",
"random-words": "0.0.1",
"resolve": "^1.1.6",
"runas": "^3.1",
"scandal": "^2.2",
"scoped-property-store": "^0.17.0",
"scrollbar-style": "^3.2",
"season": "^5.3",
"semver": "^4.3.3",
"service-hub": "^0.6.2",
"service-hub": "^0.7.0",
"source-map-support": "^0.3.2",
"stacktrace-parser": "0.1.1",
"temp": "0.8.1",
@@ -73,10 +75,10 @@
"autocomplete-atom-api": "0.9.2",
"autocomplete-css": "0.11.0",
"autocomplete-html": "0.7.2",
"autocomplete-plus": "2.22.0",
"autocomplete-plus": "2.23.0",
"autocomplete-snippets": "1.7.1",
"autoflow": "0.26.0",
"autosave": "0.22.0",
"autosave": "0.23.0",
"background-tips": "0.26.0",
"bookmarks": "0.38.0",
"bracket-matcher": "0.78.0",
@@ -85,8 +87,8 @@
"dev-live-reload": "0.47.0",
"encoding-selector": "0.21.0",
"exception-reporting": "0.37.0",
"find-and-replace": "0.185.0",
"fuzzy-finder": "0.90.0",
"find-and-replace": "0.187.1",
"fuzzy-finder": "0.91.0",
"git-diff": "0.56.0",
"go-to-line": "0.30.0",
"grammar-selector": "0.47.0",
@@ -95,26 +97,26 @@
"keybinding-resolver": "0.33.0",
"line-ending-selector": "0.2.0",
"link": "0.31.0",
"markdown-preview": "0.154.0",
"markdown-preview": "0.155.0",
"metrics": "0.52.0",
"notifications": "0.59.0",
"open-on-github": "0.38.0",
"package-generator": "0.40.0",
"release-notes": "0.53.0",
"settings-view": "0.230.1",
"snippets": "0.100.0",
"snippets": "0.101.0",
"spell-check": "0.61.0",
"status-bar": "0.79.0",
"styleguide": "0.44.0",
"symbols-view": "0.109.0",
"tabs": "0.84.0",
"tabs": "0.85.0",
"timecop": "0.33.0",
"tree-view": "0.190.0",
"tree-view": "0.192.2",
"update-package-dependencies": "0.10.0",
"welcome": "0.30.0",
"welcome": "0.31.0",
"whitespace": "0.31.0",
"wrap-guide": "0.38.0",
"language-c": "0.48.0",
"language-c": "0.49.0",
"language-clojure": "0.18.0",
"language-coffee-script": "0.42.0",
"language-csharp": "0.11.0",
@@ -125,14 +127,14 @@
"language-html": "0.42.0",
"language-hyperlink": "0.14.0",
"language-java": "0.16.0",
"language-javascript": "0.96.0",
"language-javascript": "0.97.0",
"language-json": "0.17.0",
"language-less": "0.28.2",
"language-make": "0.19.0",
"language-mustache": "0.13.0",
"language-objective-c": "0.15.0",
"language-perl": "0.30.0",
"language-php": "0.30.0",
"language-php": "0.31.0",
"language-property-list": "0.8.0",
"language-python": "0.40.0",
"language-ruby": "0.60.0",

View File

@@ -1,13 +1,20 @@
Exec = require('child_process').exec
_ = require "underscore-plus"
path = require 'path'
temp = require 'temp'
Package = require '../src/package'
ThemeManager = require '../src/theme-manager'
_ = require "underscore-plus"
temp = require "temp"
AtomEnvironment = require '../src/atom-environment'
describe "the `atom` global", ->
describe "AtomEnvironment", ->
describe 'window sizing methods', ->
describe '::getPosition and ::setPosition', ->
originalPosition = null
beforeEach ->
originalPosition = atom.getPosition()
afterEach ->
atom.setPosition(originalPosition.x, originalPosition.y)
it 'sets the position of the window, and can retrieve the position just set', ->
atom.setPosition(22, 45)
expect(atom.getPosition()).toEqual x: 22, y: 45
@@ -31,28 +38,8 @@ describe "the `atom` global", ->
version = '36b5518'
expect(atom.isReleasedVersion()).toBe false
describe "when an update becomes available", ->
subscription = null
afterEach ->
subscription?.dispose()
it "invokes onUpdateAvailable listeners", ->
updateAvailableHandler = jasmine.createSpy("update-available-handler")
subscription = atom.onUpdateAvailable updateAvailableHandler
autoUpdater = require('remote').require('auto-updater')
autoUpdater.emit 'update-downloaded', null, "notes", "version"
waitsFor ->
updateAvailableHandler.callCount > 0
runs ->
{releaseVersion} = updateAvailableHandler.mostRecentCall.args[0]
expect(releaseVersion).toBe 'version'
describe "loading default config", ->
it 'loads the default core config', ->
it 'loads the default core config schema', ->
expect(atom.config.get('core.excludeVcsIgnoredPaths')).toBe true
expect(atom.config.get('core.followSymlinks')).toBe true
expect(atom.config.get('editor.showInvisibles')).toBe false
@@ -139,7 +126,7 @@ describe "the `atom` global", ->
expect(result).toBe false
expect(errors.length).toBe 1
expect(errors[0].message).toBe "Assertion failed: a == b"
expect(errors[0].stack).toContain('atom-spec')
expect(errors[0].stack).toContain('atom-environment-spec')
describe "if passed a callback function", ->
it "calls the callback with the assertion failure's error object", ->
@@ -154,30 +141,34 @@ describe "the `atom` global", ->
expect(errors).toEqual []
describe "saving and loading", ->
afterEach -> atom.mode = "spec"
beforeEach ->
atom.enablePersistence = true
afterEach ->
atom.enablePersistence = false
it "selects the state based on the current project paths", ->
Atom = atom.constructor
[dir1, dir2] = [temp.mkdirSync("dir1-"), temp.mkdirSync("dir2-")]
loadSettings = _.extend Atom.getLoadSettings(),
loadSettings = _.extend atom.getLoadSettings(),
initialPaths: [dir1]
windowState: null
spyOn(Atom, 'getLoadSettings').andCallFake -> loadSettings
spyOn(Atom.getStorageFolder(), 'getPath').andReturn(temp.mkdirSync("storage-dir-"))
spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings
spyOn(atom.getStorageFolder(), 'getPath').andReturn(temp.mkdirSync("storage-dir-"))
atom.mode = "editor"
atom.state.stuff = "cool"
atom.project.setPaths([dir1, dir2])
atom.saveSync.originalValue.call(atom)
atom.saveStateSync()
atom1 = Atom.loadOrCreate("editor")
expect(atom1.state.stuff).toBeUndefined()
atom.state = {}
atom.loadStateSync()
expect(atom.state.stuff).toBeUndefined()
loadSettings.initialPaths = [dir2, dir1]
atom2 = Atom.loadOrCreate("editor")
expect(atom2.state.stuff).toBe("cool")
atom.state = {}
atom.loadStateSync()
expect(atom.state.stuff).toBe("cool")
describe "openInitialEmptyEditorIfNecessary", ->
describe "when there are no paths set", ->
@@ -225,28 +216,92 @@ describe "the `atom` global", ->
describe "::unloadEditorWindow()", ->
it "saves the serialized state of the window so it can be deserialized after reload", ->
workspaceState = atom.workspace.serialize()
syntaxState = atom.grammars.serialize()
projectState = atom.project.serialize()
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document})
spyOn(atomEnvironment, 'saveStateSync')
atom.unloadEditorWindow()
workspaceState = atomEnvironment.workspace.serialize()
grammarsState = {grammarOverridesByPath: atomEnvironment.grammars.grammarOverridesByPath}
projectState = atomEnvironment.project.serialize()
expect(atom.state.workspace).toEqual workspaceState
expect(atom.state.grammars).toEqual syntaxState
expect(atom.state.project).toEqual projectState
expect(atom.saveSync).toHaveBeenCalled()
atomEnvironment.unloadEditorWindow()
describe "::removeEditorWindow()", ->
expect(atomEnvironment.state.workspace).toEqual workspaceState
expect(atomEnvironment.state.grammars).toEqual grammarsState
expect(atomEnvironment.state.project).toEqual projectState
expect(atomEnvironment.saveStateSync).toHaveBeenCalled()
atomEnvironment.destroy()
describe "::destroy()", ->
it "unsubscribes from all buffers", ->
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document})
waitsForPromise ->
atom.workspace.open("sample.js")
atomEnvironment.workspace.open("sample.js")
runs ->
buffer = atom.workspace.getActivePaneItem().buffer
pane = atom.workspace.getActivePane()
buffer = atomEnvironment.workspace.getActivePaneItem().buffer
pane = atomEnvironment.workspace.getActivePane()
pane.splitRight(copyActiveItem: true)
expect(atom.workspace.getTextEditors().length).toBe 2
expect(atomEnvironment.workspace.getTextEditors().length).toBe 2
atom.removeEditorWindow()
atomEnvironment.destroy()
expect(buffer.getSubscriptionCount()).toBe 0
describe "::openLocations(locations) (called via IPC from browser process)", ->
beforeEach ->
spyOn(atom.workspace, 'open')
atom.project.setPaths([])
describe "when the opened path exists", ->
it "adds it to the project's paths", ->
pathToOpen = __filename
atom.openLocations([{pathToOpen}])
expect(atom.project.getPaths()[0]).toBe __dirname
describe "when the opened path does not exist but its parent directory does", ->
it "adds the parent directory to the project paths", ->
pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt')
atom.openLocations([{pathToOpen}])
expect(atom.project.getPaths()[0]).toBe __dirname
describe "when the opened path is a file", ->
it "opens it in the workspace", ->
pathToOpen = __filename
atom.openLocations([{pathToOpen}])
expect(atom.workspace.open.mostRecentCall.args[0]).toBe __filename
describe "when the opened path is a directory", ->
it "does not open it in the workspace", ->
pathToOpen = __dirname
atom.openLocations([{pathToOpen}])
expect(atom.workspace.open.callCount).toBe 0
describe "when the opened path is a uri", ->
it "adds it to the project's paths as is", ->
pathToOpen = 'remote://server:7644/some/dir/path'
atom.openLocations([{pathToOpen}])
expect(atom.project.getPaths()[0]).toBe pathToOpen
describe "::updateAvailable(info) (called via IPC from browser process)", ->
subscription = null
afterEach ->
subscription?.dispose()
it "invokes onUpdateAvailable listeners", ->
atom.listenForUpdates()
updateAvailableHandler = jasmine.createSpy("update-available-handler")
subscription = atom.onUpdateAvailable updateAvailableHandler
autoUpdater = require('remote').require('auto-updater')
autoUpdater.emit 'update-downloaded', null, "notes", "version"
waitsFor ->
updateAvailableHandler.callCount > 0
runs ->
{releaseVersion} = updateAvailableHandler.mostRecentCall.args[0]
expect(releaseVersion).toBe 'version'

View File

@@ -22,7 +22,8 @@ describe "CommandInstaller on #darwin", ->
describe "when using a stable version of atom", ->
beforeEach ->
installer = new CommandInstaller("2.0.2")
confirm = ->
installer = new CommandInstaller("2.0.2", confirm)
it "symlinks the atom command as 'atom'", ->
installedAtomPath = path.join(installationPath, 'atom')

View File

@@ -16,6 +16,7 @@ describe "CommandRegistry", ->
document.querySelector('#jasmine-content').appendChild(parent)
registry = new CommandRegistry
registry.attach(parent)
afterEach ->
registry.destroy()
@@ -277,3 +278,18 @@ describe "CommandRegistry", ->
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'}
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
]
describe "::attach(rootNode)", ->
it "adds event listeners for any previously-added commands", ->
registry2 = new CommandRegistry
commandSpy = jasmine.createSpy('command-callback')
registry2.add '.grandchild', 'command-1', commandSpy
grandchild.dispatchEvent(new CustomEvent('command-1', bubbles: true))
expect(commandSpy).not.toHaveBeenCalled()
registry2.attach(parent)
grandchild.dispatchEvent(new CustomEvent('command-1', bubbles: true))
expect(commandSpy).toHaveBeenCalled()

View File

@@ -7,10 +7,16 @@ describe "Config", ->
dotAtomPath = null
beforeEach ->
spyOn(atom.config, "load")
spyOn(atom.config, "save")
dotAtomPath = temp.path('dot-atom-dir')
atom.config.configDirPath = dotAtomPath
atom.config.enablePersistence = true
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
afterEach ->
atom.config.enablePersistence = false
describe ".get(keyPath, {scope, sources, excludeSources})", ->
it "allows a key path's value to be read", ->
expect(atom.config.set("foo.bar.baz", 42)).toBe true

View File

@@ -5,7 +5,7 @@ describe "ContextMenuManager", ->
beforeEach ->
{resourcePath} = atom.getLoadSettings()
contextMenu = new ContextMenuManager({resourcePath})
contextMenu = new ContextMenuManager({resourcePath, keymapManager: atom.keymaps})
parent = document.createElement("div")
child = document.createElement("div")

View File

@@ -7,7 +7,7 @@ describe "CustomGutterComponent", ->
beforeEach ->
mockGutterContainer = {}
gutter = new Gutter(mockGutterContainer, {name: 'test-gutter'})
gutterComponent = new CustomGutterComponent({gutter})
gutterComponent = new CustomGutterComponent({gutter, views: atom.views})
it "creates a gutter DOM node with only an empty 'custom-decorations' child node when it is initialized", ->
expect(gutterComponent.getDomNode().classList.contains('gutter')).toBe true

View File

@@ -7,7 +7,10 @@ describe "DisplayBuffer", ->
tabLength = 2
buffer = atom.project.bufferForPathSync('sample.js')
displayBuffer = new DisplayBuffer({buffer, tabLength})
displayBuffer = new DisplayBuffer({
buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: ->
})
changeHandler = jasmine.createSpy 'changeHandler'
displayBuffer.onDidChange changeHandler
@@ -49,7 +52,10 @@ describe "DisplayBuffer", ->
it "updates the display buffer prior to invoking change handlers registered on the buffer", ->
buffer.onDidChange -> expect(displayBuffer2.tokenizedLineForScreenRow(0).text).toBe "testing"
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
displayBuffer2 = new DisplayBuffer({
buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: ->
})
buffer.setText("testing")
describe "soft wrapping", ->
@@ -263,7 +269,10 @@ describe "DisplayBuffer", ->
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
it "correctly renders the original wrapped line", ->
buffer = atom.project.buildBufferSync(null, '')
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30})
displayBuffer = new DisplayBuffer({
buffer, tabLength, editorWidthInChars: 30, config: atom.config,
grammarRegistry: atom.grammars, packageManager: atom.packages, assert: ->
})
displayBuffer.setDefaultCharWidth(1)
displayBuffer.setSoftWrapped(true)
@@ -326,7 +335,10 @@ describe "DisplayBuffer", ->
displayBuffer.destroy()
buffer.release()
buffer = atom.project.bufferForPathSync('two-hundred.txt')
displayBuffer = new DisplayBuffer({buffer, tabLength})
displayBuffer = new DisplayBuffer({
buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: ->
})
displayBuffer.onDidChange changeHandler
describe "when folds are created and destroyed", ->
@@ -440,7 +452,10 @@ describe "DisplayBuffer", ->
describe "when there is another display buffer pointing to the same buffer", ->
it "does not consider folds to be nested inside of folds from the other display buffer", ->
otherDisplayBuffer = new DisplayBuffer({buffer, tabLength})
otherDisplayBuffer = new DisplayBuffer({
buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: ->
})
otherDisplayBuffer.createFold(1, 5)
displayBuffer.createFold(2, 4)
@@ -1188,7 +1203,10 @@ describe "DisplayBuffer", ->
describe 'when there are multiple DisplayBuffers for a buffer', ->
describe 'when a marker is created', ->
it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', ->
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
displayBuffer2 = new DisplayBuffer({
buffer, tabLength, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: ->
})
displayBuffer.onDidCreateMarker markerCreated1 = jasmine.createSpy().andCallFake (marker) -> marker.destroy()
displayBuffer2.onDidCreateMarker markerCreated2 = jasmine.createSpy()

View File

@@ -6,11 +6,15 @@ GitRepository = require '../src/git-repository'
GitRepositoryProvider = require '../src/git-repository-provider'
describe "GitRepositoryProvider", ->
provider = null
beforeEach ->
provider = new GitRepositoryProvider(atom.project, atom.config, atom.confirm)
describe ".repositoryForDirectory(directory)", ->
describe "when specified a Directory with a Git repository", ->
it "returns a Promise that resolves to a GitRepository", ->
waitsForPromise ->
provider = new GitRepositoryProvider atom.project
directory = new Directory path.join(__dirname, 'fixtures', 'git', 'master.git')
provider.repositoryForDirectory(directory).then (result) ->
expect(result).toBeInstanceOf GitRepository
@@ -19,7 +23,6 @@ describe "GitRepositoryProvider", ->
expect(result.getType()).toBe 'git'
it "returns the same GitRepository for different Directory objects in the same repo", ->
provider = new GitRepositoryProvider atom.project
firstRepo = null
secondRepo = null
@@ -38,7 +41,6 @@ describe "GitRepositoryProvider", ->
describe "when specified a Directory without a Git repository", ->
it "returns a Promise that resolves to null", ->
waitsForPromise ->
provider = new GitRepositoryProvider atom.project
directory = new Directory temp.mkdirSync('dir')
provider.repositoryForDirectory(directory).then (result) ->
expect(result).toBe null
@@ -46,7 +48,6 @@ describe "GitRepositoryProvider", ->
describe "when specified a Directory with an invalid Git repository", ->
it "returns a Promise that resolves to null", ->
waitsForPromise ->
provider = new GitRepositoryProvider atom.project
dirPath = temp.mkdirSync('dir')
fs.writeFileSync(path.join(dirPath, '.git', 'objects'), '')
fs.writeFileSync(path.join(dirPath, '.git', 'HEAD'), '')
@@ -59,7 +60,6 @@ describe "GitRepositoryProvider", ->
describe "when specified a Directory with a valid gitfile-linked repository", ->
it "returns a Promise that resolves to a GitRepository", ->
waitsForPromise ->
provider = new GitRepositoryProvider atom.project
gitDirPath = path.join(__dirname, 'fixtures', 'git', 'master.git')
workDirPath = temp.mkdirSync('git-workdir')
fs.writeFileSync(path.join(workDirPath, '.git'), 'gitdir: ' + gitDirPath+'\n')
@@ -75,8 +75,6 @@ describe "GitRepositoryProvider", ->
directory = null
provider = null
beforeEach ->
provider = new GitRepositoryProvider atom.project
# An implementation of Directory that does not implement existsSync().
subdirectory = {}
directory =

View File

@@ -124,8 +124,10 @@ describe "GitRepository", ->
[filePath, editor] = []
beforeEach ->
spyOn(atom, "confirm")
workingDirPath = copyRepository()
repo = new GitRepository(workingDirPath)
repo = new GitRepository(workingDirPath, {project: atom.project, config: atom.config, confirm: atom.confirm})
filePath = path.join(workingDirPath, 'a.txt')
fs.writeFileSync(filePath, 'ch ch changes')
@@ -136,7 +138,7 @@ describe "GitRepository", ->
editor = atom.workspace.getActiveTextEditor()
it "displays a confirmation dialog by default", ->
spyOn(atom, 'confirm').andCallFake ({buttons}) -> buttons.OK()
atom.confirm.andCallFake ({buttons}) -> buttons.OK()
atom.config.set('editor.confirmCheckoutHeadRevision', true)
repo.checkoutHeadForEditor(editor)
@@ -144,7 +146,6 @@ describe "GitRepository", ->
expect(fs.readFileSync(filePath, 'utf8')).toBe ''
it "does not display a dialog when confirmation is disabled", ->
spyOn(atom, 'confirm')
atom.config.set('editor.confirmCheckoutHeadRevision', false)
repo.checkoutHeadForEditor(editor)
@@ -277,7 +278,8 @@ describe "GitRepository", ->
atom.workspace.open('file.txt')
runs ->
project2 = Project.deserialize(atom.project.serialize())
project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
project2.deserialize(atom.project.serialize(), atom.deserializers)
buffer = project2.getBuffers()[0]
waitsFor ->

View File

@@ -24,18 +24,9 @@ describe "the `grammars` global", ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
describe "serialization", ->
it "remembers grammar overrides by path", ->
filePath = '/foo/bar/file.js'
expect(atom.grammars.selectGrammar(filePath).name).not.toBe 'Ruby'
atom.grammars.setGrammarOverrideForPath(filePath, 'source.ruby')
grammars2 = atom.deserializers.deserialize(atom.grammars.serialize())
grammars2.addGrammar(grammar) for grammar in atom.grammars.grammars when grammar isnt atom.grammars.nullGrammar
expect(grammars2.selectGrammar(filePath).name).toBe 'Ruby'
describe ".selectGrammar(filePath)", ->
it "always returns a grammar", ->
registry = new GrammarRegistry()
registry = new GrammarRegistry(config: atom.config)
expect(registry.selectGrammar().scopeName).toBe 'text.plain.null-grammar'
it "selects the text.plain grammar over the null grammar", ->

View File

@@ -26,7 +26,7 @@ describe "GutterContainerComponent", ->
domElementPool = new DOMElementPool
mockEditor = {}
mockMouseDown = ->
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown, domElementPool})
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown, domElementPool, views: atom.views})
it "creates a DOM node with no child gutter nodes when it is initialized", ->
expect(gutterContainerComponent.getDomNode() instanceof HTMLElement).toBe true

View File

@@ -91,7 +91,8 @@ buildAtomClient = (args, env) ->
cb(null)
.addCommand "treeViewRootDirectories", (cb) ->
@execute(->
@waitForExist('.tree-view', 10000)
.execute(->
for element in document.querySelectorAll(".tree-view .project-root > .header .name")
element.dataset.path
, cb)
@@ -104,7 +105,8 @@ buildAtomClient = (args, env) ->
.then ({value: newWindowHandles}) ->
[newWindowHandle] = difference(newWindowHandles, oldWindowHandles)
return done() unless newWindowHandle
@window(newWindowHandle, done)
@window(newWindowHandle)
.waitForExist('atom-workspace', 10000, done)
.addCommand "startAnotherAtom", (args, env, done) ->
@call ->
@@ -168,7 +170,11 @@ module.exports = (args, env, fn) ->
jasmine.getEnv().currentSpec.fail(new Error(err.response?.body?.value?.message))
finish()
fn(client.init()).then(finish)
fn(
client.init()
.waitUntil((-> @windowHandles().then ({value}) -> value.length > 0), 10000)
.waitForExist("atom-workspace", 10000)
).then(finish)
, 30000)
waitsFor("webdriver to stop", chromeDriverDown, 15000)

View File

@@ -28,8 +28,6 @@ describe "Starting Atom", ->
it "opens the parent directory and creates an empty text editor", ->
runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 1000)
.treeViewRootDirectories()
@@ -54,8 +52,6 @@ describe "Starting Atom", ->
runAtom ["#{filePath}:3"], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 1000)
.waitForExist("atom-text-editor", 5000)
.then (exists) -> expect(exists).toBe true
@@ -79,8 +75,6 @@ describe "Starting Atom", ->
runAtom ["#{filePath}:2:2"], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 1000)
.waitForExist("atom-text-editor", 5000)
.then (exists) -> expect(exists).toBe true
@@ -97,8 +91,6 @@ describe "Starting Atom", ->
filePath = path.join(tempDirPath, "new-file")
runAtom ["#{filePath}: "], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 1000)
.waitForExist("atom-text-editor", 5000)
.then (exists) -> expect(exists).toBe true
@@ -113,8 +105,6 @@ describe "Starting Atom", ->
runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 5000)
# Opening another file reuses the same window and does not change the
@@ -131,7 +121,6 @@ describe "Starting Atom", ->
.waitForNewWindow(->
@startAnotherAtom([otherTempDirPath], ATOM_HOME: atomHome)
, 5000)
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(0, 1000)
.treeViewRootDirectories()
.then ({value}) -> expect(value).toEqual([otherTempDirPath])
@@ -140,14 +129,12 @@ describe "Starting Atom", ->
it "remembers the state of the window", ->
runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(0, 3000)
.execute -> atom.workspace.open()
.waitForPaneItemCount(1, 3000)
runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace", 5000)
.waitForPaneItemCount(1, 5000)
describe "opening multiple directories simultaneously", ->
@@ -157,14 +144,12 @@ describe "Starting Atom", ->
runAtom [tempDirPath, otherTempDirPath], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace", 5000)
.treeViewRootDirectories()
.then ({value}) -> expect(value).toEqual([tempDirPath, otherTempDirPath])
# Opening one of those directories again reuses the same window and
# does not change the project paths.
.startAnotherAtom([nestedDir], ATOM_HOME: atomHome)
.waitForExist("atom-workspace", 5000)
.treeViewRootDirectories()
.then ({value}) -> expect(value).toEqual([tempDirPath, otherTempDirPath])
@@ -172,7 +157,6 @@ describe "Starting Atom", ->
it "reuses that window to open a directory", ->
runAtom [], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace")
.treeViewRootDirectories()
.then ({value}) -> expect(value).toEqual([])
@@ -188,7 +172,6 @@ describe "Starting Atom", ->
it "opens a new window with a single untitled buffer", ->
runAtom [], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace")
.waitForPaneItemCount(1, 5000)
# Opening with no file paths always creates a new window, even if
@@ -196,7 +179,6 @@ describe "Starting Atom", ->
.waitForNewWindow(->
@startAnotherAtom([], ATOM_HOME: atomHome)
, 5000)
.waitForExist("atom-workspace")
.waitForPaneItemCount(1, 5000)
it "doesn't open a new window if openEmptyEditorOnStart is disabled", ->
@@ -207,17 +189,14 @@ describe "Starting Atom", ->
runAtom [], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace")
.waitForPaneItemCount(0, 5000)
it "reopens any previously opened windows", ->
runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) ->
client
.waitForExist("atom-workspace")
.waitForNewWindow(->
@startAnotherAtom([otherTempDirPath], ATOM_HOME: atomHome)
, 5000)
.waitForExist("atom-workspace")
runAtom [], {ATOM_HOME: atomHome}, (client) ->
windowProjectPaths = []
@@ -226,12 +205,10 @@ describe "Starting Atom", ->
.waitForWindowCount(2, 10000)
.then ({value: windowHandles}) ->
@window(windowHandles[0])
.waitForExist("atom-workspace")
.treeViewRootDirectories()
.then ({value: directories}) -> windowProjectPaths.push(directories)
.window(windowHandles[1])
.waitForExist("atom-workspace")
.treeViewRootDirectories()
.then ({value: directories}) -> windowProjectPaths.push(directories)
@@ -246,7 +223,5 @@ describe "Starting Atom", ->
remoteDirectory = 'remote://server:3437/some/directory/path'
runAtom [remoteDirectory], {ATOM_HOME: atomHome}, (client) ->
client
.waitForWindowCount(1, 1000)
.waitForExist("atom-workspace", 5000)
.treeViewRootDirectories()
.then ({value}) -> expect(value).toEqual([remoteDirectory])

View File

@@ -1,59 +0,0 @@
fs = require 'fs'
module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
window[key] = value for key, value of require '../vendor/jasmine'
{TerminalReporter} = require 'jasmine-tagged'
disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI
TimeReporter = require './time-reporter'
timeReporter = new TimeReporter()
logStream = fs.openSync(logFile, 'w') if logFile?
log = (str) ->
if logStream?
fs.writeSync(logStream, str)
else
process.stderr.write(str)
if atom.getLoadSettings().exitWhenDone
reporter = new TerminalReporter
print: (str) ->
log(str)
onComplete: (runner) ->
fs.closeSync(logStream) if logStream?
if process.env.JANKY_SHA1 or process.env.CI
grim = require 'grim'
if grim.getDeprecationsLength() > 0
grim.logDeprecations()
return atom.exit(1)
if runner.results().failedCount > 0
atom.exit(1)
else
atom.exit(0)
else
AtomReporter = require './atom-reporter'
reporter = new AtomReporter()
require specSuite
jasmineEnv = jasmine.getEnv()
jasmineEnv.addReporter(reporter)
jasmineEnv.addReporter(timeReporter)
jasmineEnv.setIncludedTags([process.platform])
jasmineContent = document.createElement('div')
jasmineContent.setAttribute('id', 'jasmine-content')
document.body.appendChild(jasmineContent)
jasmineEnv.execute()
disableFocusMethods = ->
['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) ->
focusMethod = window[methodName]
window[methodName] = (description) ->
error = new Error('Focused spec is running on CI')
focusMethod description, -> throw error

View File

@@ -0,0 +1,111 @@
fs = require 'fs'
_ = require 'underscore-plus'
fs = require 'fs-plus'
path = require 'path'
ipc = require 'ipc'
module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) ->
window[key] = value for key, value of require '../vendor/jasmine'
require 'jasmine-tagged'
# Allow document.title to be assigned in specs without screwing up spec window title
documentTitle = null
Object.defineProperty document, 'title',
get: -> documentTitle
set: (title) -> documentTitle = title
ApplicationDelegate = require '../src/application-delegate'
applicationDelegate = new ApplicationDelegate()
applicationDelegate.setRepresentedFilename = ->
applicationDelegate.setWindowDocumentEdited = ->
window.atom = buildAtomEnvironment({
applicationDelegate, window, document,
configDirPath: process.env.ATOM_HOME
enablePersistence: false
})
require './spec-helper'
disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI
requireSpecs(testPath) for testPath in testPaths
setSpecType('user')
resolveWithExitCode = null
promise = new Promise (resolve, reject) -> resolveWithExitCode = resolve
jasmineEnv = jasmine.getEnv()
jasmineEnv.addReporter(buildReporter({logFile, headless, resolveWithExitCode}))
TimeReporter = require './time-reporter'
jasmineEnv.addReporter(new TimeReporter())
jasmineEnv.setIncludedTags([process.platform])
jasmineContent = document.createElement('div')
jasmineContent.setAttribute('id', 'jasmine-content')
document.body.appendChild(jasmineContent)
jasmineEnv.execute()
promise
disableFocusMethods = ->
['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) ->
focusMethod = window[methodName]
window[methodName] = (description) ->
error = new Error('Focused spec is running on CI')
focusMethod description, -> throw error
requireSpecs = (testPath, specType) ->
if fs.isDirectorySync(testPath)
for testFilePath in fs.listTreeSync(testPath) when /-spec\.(coffee|js)$/.test testFilePath
require(testFilePath)
# Set spec directory on spec for setting up the project in spec-helper
setSpecDirectory(testPath)
else
require(testPath)
setSpecDirectory(path.dirname(testPath))
setSpecField = (name, value) ->
specs = jasmine.getEnv().currentRunner().specs()
return if specs.length is 0
for index in [specs.length-1..0]
break if specs[index][name]?
specs[index][name] = value
setSpecType = (specType) ->
setSpecField('specType', specType)
setSpecDirectory = (specDirectory) ->
setSpecField('specDirectory', specDirectory)
buildReporter = ({logFile, headless, resolveWithExitCode}) ->
if headless
buildTerminalReporter(logFile, resolveWithExitCode)
else
AtomReporter = require './atom-reporter'
reporter = new AtomReporter()
buildTerminalReporter = (logFile, resolveWithExitCode) ->
logStream = fs.openSync(logFile, 'w') if logFile?
log = (str) ->
if logStream?
fs.writeSync(logStream, str)
else
ipc.send 'write-to-stderr', str
{TerminalReporter} = require 'jasmine-tagged'
new TerminalReporter
print: (str) ->
log(str)
onComplete: (runner) ->
fs.closeSync(logStream) if logStream?
if process.env.JANKY_SHA1 or process.env.CI
grim = require 'grim'
if grim.getDeprecationsLength() > 0
grim.logDeprecations()
resolveWithExitCode(1)
return
if runner.results().failedCount > 0
resolveWithExitCode(1)
else
resolveWithExitCode(0)

View File

@@ -9,7 +9,7 @@ describe "LinesYardstick", ->
atom.packages.activatePackage('language-javascript')
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
createdLineNodes = []
@@ -56,7 +56,7 @@ describe "LinesYardstick", ->
textNodes
editor.setLineHeightInPixels(14)
linesYardstick = new LinesYardstick(editor, mockPresenter, mockLineNodesProvider)
linesYardstick = new LinesYardstick(editor, mockPresenter, mockLineNodesProvider, atom.grammars)
afterEach ->
lineNode.remove() for lineNode in createdLineNodes

View File

@@ -5,7 +5,11 @@ describe "MenuManager", ->
menu = null
beforeEach ->
menu = new MenuManager(resourcePath: atom.getLoadSettings().resourcePath)
menu = new MenuManager(
resourcePath: atom.getLoadSettings().resourcePath
keymapManager: atom.keymaps
packageManager: atom.packages
)
describe "::add(items)", ->
it "can add new menus that can be removed with the returned disposable", ->
@@ -67,7 +71,7 @@ describe "MenuManager", ->
atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b'
atom.keymaps.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!'
waits 1
waits 50
runs -> expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined()

View File

@@ -770,8 +770,6 @@ describe "PackageManager", ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
GrammarRegistry = require '../src/grammar-registry'
atom.grammars = window.syntax = new GrammarRegistry()
jasmine.restoreDeprecationsSnapshot()
it "activates all the packages, and none of the themes", ->

View File

@@ -3,9 +3,22 @@ Package = require '../src/package'
ThemePackage = require '../src/theme-package'
describe "Package", ->
build = (constructor, path) ->
new constructor(
path: path, packageManager: atom.packages, config: atom.config,
styleManager: atom.styles, notificationManager: atom.notifications,
keymapManager: atom.keymaps, commandRegistry: atom.command,
grammarRegistry: atom.grammars, themeManager: atom.themes,
menuManager: atom.menu, contextMenuManager: atom.contextMenu,
devMode: false
)
buildPackage = (packagePath) -> build(Package, packagePath)
buildThemePackage = (themePath) -> build(ThemePackage, themePath)
describe "when the package contains incompatible native modules", ->
beforeEach ->
spyOn(atom, 'inDevMode').andReturn(false)
items = {}
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
@@ -13,35 +26,34 @@ describe "Package", ->
it "does not activate it", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
expect(pack.isCompatible()).toBe false
expect(pack.incompatibleModules[0].name).toBe 'native-module'
expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module')
it "utilizes _atomModuleCache if present to determine the package's native dependencies", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-ignored-incompatible-native-module')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
expect(pack.getNativeModuleDependencyPaths().length).toBe(1) # doesn't see the incompatible module
expect(pack.isCompatible()).toBe true
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-cached-incompatible-native-module')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
expect(pack.isCompatible()).toBe false
it "caches the incompatible native modules in local storage", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
expect(new Package(packagePath).isCompatible()).toBe false
expect(buildPackage(packagePath).isCompatible()).toBe false
expect(global.localStorage.getItem.callCount).toBe 1
expect(global.localStorage.setItem.callCount).toBe 1
expect(new Package(packagePath).isCompatible()).toBe false
expect(buildPackage(packagePath).isCompatible()).toBe false
expect(global.localStorage.getItem.callCount).toBe 2
expect(global.localStorage.setItem.callCount).toBe 1
describe "::rebuild()", ->
beforeEach ->
spyOn(atom, 'inDevMode').andReturn(false)
items = {}
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
@@ -49,7 +61,7 @@ describe "Package", ->
it "returns a promise resolving to the results of `apm rebuild`", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
rebuildCallbacks = []
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
@@ -63,7 +75,7 @@ describe "Package", ->
it "persists build failures in local storage", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
expect(pack.isCompatible()).toBe true
expect(pack.getBuildFailureOutput()).toBeNull()
@@ -79,7 +91,7 @@ describe "Package", ->
expect(pack.isCompatible()).toBe false
# A different package instance has the same failure output (simulates reload)
pack2 = new Package(packagePath)
pack2 = buildPackage(packagePath)
expect(pack2.getBuildFailureOutput()).toBe 'It is broken'
expect(pack2.isCompatible()).toBe false
@@ -92,7 +104,7 @@ describe "Package", ->
it "sets cached incompatible modules to an empty array when the rebuild completes (there may be a build error, but rebuilding *deletes* native modules)", ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
pack = new Package(packagePath)
pack = buildPackage(packagePath)
expect(pack.getIncompatibleNativeModules().length).toBeGreaterThan(0)
@@ -118,14 +130,14 @@ describe "Package", ->
it "loads and applies css", ->
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-css')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
expect(getComputedStyle(editorElement).paddingTop).toBe "1234px"
it "parses, loads and applies less", ->
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-less')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
expect(getComputedStyle(editorElement).paddingTop).toBe "4321px"
@@ -136,7 +148,7 @@ describe "Package", ->
expect(getComputedStyle(editorElement).paddingBottom).not.toBe("103px")
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-package-file')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
expect(getComputedStyle(editorElement).paddingTop).toBe("101px")
expect(getComputedStyle(editorElement).paddingRight).toBe("102px")
@@ -149,7 +161,7 @@ describe "Package", ->
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "30px"
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-without-package-file')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
expect(getComputedStyle(editorElement).paddingTop).toBe "10px"
expect(getComputedStyle(editorElement).paddingRight).toBe "20px"
@@ -158,7 +170,7 @@ describe "Package", ->
describe "reloading a theme", ->
beforeEach ->
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-package-file')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
it "reloads without readding to the stylesheets list", ->
@@ -169,7 +181,7 @@ describe "Package", ->
describe "events", ->
beforeEach ->
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-package-file')
theme = new ThemePackage(themePath)
theme = buildThemePackage(themePath)
theme.activate()
it "deactivated event fires on .deactivate()", ->
@@ -182,7 +194,7 @@ describe "Package", ->
beforeEach ->
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-different-directory-name')
metadata = Package.loadMetadata(packagePath, true)
metadata = atom.packages.loadPackageMetadata(packagePath, true)
it "uses the package name defined in package.json", ->
expect(metadata.name).toBe 'package-with-a-totally-different-name'

View File

@@ -9,7 +9,7 @@ describe "PaneContainerElement", ->
child.nodeName.toLowerCase() for child in paneAxisElement.children
paneAxis = new PaneAxis
paneAxisElement = new PaneAxisElement().initialize(paneAxis)
paneAxisElement = new PaneAxisElement().initialize(paneAxis, atom)
expect(childTagNames()).toEqual []
@@ -42,7 +42,7 @@ describe "PaneContainerElement", ->
]
it "transfers focus to the next pane if a focused pane is removed", ->
container = new PaneContainer
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
containerElement = atom.views.getView(container)
leftPane = container.getActivePane()
leftPaneElement = atom.views.getView(leftPane)
@@ -58,10 +58,10 @@ describe "PaneContainerElement", ->
describe "when a pane is split", ->
it "builds appropriately-oriented atom-pane-axis elements", ->
container = new PaneContainer
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
containerElement = atom.views.getView(container)
pane1 = container.getRoot()
pane1 = container.getActivePane()
pane2 = pane1.splitRight()
pane3 = pane2.splitDown()
@@ -84,7 +84,7 @@ describe "PaneContainerElement", ->
[container, containerElement] = []
beforeEach ->
container = new PaneContainer
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
containerElement = atom.views.getView(container)
document.querySelector('#jasmine-content').appendChild(containerElement)
@@ -201,7 +201,7 @@ describe "PaneContainerElement", ->
[leftPane, rightPane] = []
beforeEach ->
container = new PaneContainer
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
leftPane = container.getActivePane()
rightPane = leftPane.splitRight()
@@ -252,8 +252,8 @@ describe "PaneContainerElement", ->
element.tabIndex = -1
element
container = new PaneContainer
pane1 = container.getRoot()
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
pane1 = container.getActivePane()
pane1.activateItem(buildElement('1'))
pane4 = pane1.splitDown(items: [buildElement('4')])
pane7 = pane4.splitDown(items: [buildElement('7')])

View File

@@ -2,6 +2,16 @@ PaneContainer = require '../src/pane-container'
Pane = require '../src/pane'
describe "PaneContainer", ->
[confirm, params] = []
beforeEach ->
confirm = spyOn(atom.applicationDelegate, 'confirm').andReturn(0)
params = {
config: atom.config,
deserializerManager: atom.deserializers
applicationDelegate: atom.applicationDelegate
}
describe "serialization", ->
[containerA, pane1A, pane2A, pane3A] = []
@@ -12,8 +22,9 @@ describe "PaneContainer", ->
@deserialize: -> new this
serialize: -> deserializer: 'Item'
pane1A = new Pane(items: [new Item])
containerA = new PaneContainer(root: pane1A)
containerA = new PaneContainer(params)
pane1A = containerA.getActivePane()
pane1A.addItem(new Item)
pane2A = pane1A.splitRight(items: [new Item])
pane3A = pane2A.splitDown(items: [new Item])
pane3A.focus()
@@ -21,7 +32,8 @@ describe "PaneContainer", ->
it "preserves the focused pane across serialization", ->
expect(pane3A.focused).toBe true
containerB = PaneContainer.deserialize(containerA.serialize())
containerB = new PaneContainer(params)
containerB.deserialize(containerA.serialize(), atom.deserializers)
[pane1B, pane2B, pane3B] = containerB.getPanes()
expect(pane3B.focused).toBe true
@@ -29,7 +41,8 @@ describe "PaneContainer", ->
pane3A.activate()
expect(containerA.getActivePane()).toBe pane3A
containerB = PaneContainer.deserialize(containerA.serialize())
containerB = new PaneContainer(params)
containerB.deserialize(containerA.serialize(), atom.deserializers)
[pane1B, pane2B, pane3B] = containerB.getPanes()
expect(containerB.getActivePane()).toBe pane3B
@@ -37,7 +50,8 @@ describe "PaneContainer", ->
pane3A.activate()
state = containerA.serialize()
state.activePaneId = -22
containerB = atom.deserializers.deserialize(state)
containerB = new PaneContainer(params)
containerB.deserialize(state, atom.deserializers)
expect(containerB.getActivePane()).toBe containerB.getPanes()[0]
describe "if there are empty panes after deserialization", ->
@@ -47,7 +61,8 @@ describe "PaneContainer", ->
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
it "leaves the empty panes intact", ->
state = containerA.serialize()
containerB = atom.deserializers.deserialize(state)
containerB = new PaneContainer(params)
containerB.deserialize(state, atom.deserializers)
[leftPane, column] = containerB.getRoot().getChildren()
[topPane, bottomPane] = column.getChildren()
@@ -60,14 +75,15 @@ describe "PaneContainer", ->
atom.config.set('core.destroyEmptyPanes', true)
state = containerA.serialize()
containerB = atom.deserializers.deserialize(state)
containerB = new PaneContainer(params)
containerB.deserialize(state, atom.deserializers)
[leftPane, rightPane] = containerB.getRoot().getChildren()
expect(leftPane.getItems().length).toBe 1
expect(rightPane.getItems().length).toBe 1
it "does not allow the root pane to be destroyed", ->
container = new PaneContainer
container = new PaneContainer(params)
container.getRoot().destroy()
expect(container.getRoot()).toBeDefined()
expect(container.getRoot().isDestroyed()).toBe false
@@ -76,7 +92,7 @@ describe "PaneContainer", ->
[container, pane1, pane2] = []
beforeEach ->
container = new PaneContainer
container = new PaneContainer(params)
pane1 = container.getRoot()
it "returns the first pane if no pane has been made active", ->
@@ -105,7 +121,8 @@ describe "PaneContainer", ->
[container, pane1, pane2, observed] = []
beforeEach ->
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
container = new PaneContainer(params)
container.getRoot().addItems([new Object, new Object])
container.getRoot().splitRight(items: [new Object, new Object])
[pane1, pane2] = container.getPanes()
@@ -147,7 +164,7 @@ describe "PaneContainer", ->
describe "::observePanes()", ->
it "invokes observers with all current and future panes", ->
container = new PaneContainer
container = new PaneContainer(params)
container.getRoot().splitRight()
[pane1, pane2] = container.getPanes()
@@ -161,7 +178,8 @@ describe "PaneContainer", ->
describe "::observePaneItems()", ->
it "invokes observers with all current and future pane items", ->
container = new PaneContainer(root: new Pane(items: [new Object, new Object]))
container = new PaneContainer(params)
container.getRoot().addItems([new Object, new Object])
container.getRoot().splitRight(items: [new Object])
[pane1, pane2] = container.getPanes()
observed = []
@@ -180,27 +198,27 @@ describe "PaneContainer", ->
shouldPromptToSave: -> true
getURI: -> 'test'
container = new PaneContainer
container = new PaneContainer(params)
container.getRoot().splitRight()
[pane1, pane2] = container.getPanes()
pane1.addItem(new TestItem)
pane2.addItem(new TestItem)
it "returns true if the user saves all modified files when prompted", ->
spyOn(atom, "confirm").andReturn(0)
confirm.andReturn(0)
saved = container.confirmClose()
expect(saved).toBeTruthy()
expect(atom.confirm).toHaveBeenCalled()
expect(confirm).toHaveBeenCalled()
it "returns false if the user cancels saving any modified file", ->
spyOn(atom, "confirm").andReturn(1)
confirm.andReturn(1)
saved = container.confirmClose()
expect(saved).toBeFalsy()
expect(atom.confirm).toHaveBeenCalled()
expect(confirm).toHaveBeenCalled()
describe "::onDidAddPane(callback)", ->
it "invokes the given callback when panes are added", ->
container = new PaneContainer
container = new PaneContainer(params)
events = []
container.onDidAddPane (event) -> events.push(event)
@@ -217,7 +235,7 @@ describe "PaneContainer", ->
destroy: -> @_isDestroyed = true
isDestroyed: -> @_isDestroyed
container = new PaneContainer
container = new PaneContainer(params)
events = []
container.onWillDestroyPane (event) ->
itemsDestroyed = (item.isDestroyed() for item in event.pane.getItems())
@@ -233,7 +251,7 @@ describe "PaneContainer", ->
describe "::onDidDestroyPane(callback)", ->
it "invokes the given callback when panes are destroyed", ->
container = new PaneContainer
container = new PaneContainer(params)
events = []
container.onDidDestroyPane (event) -> events.push(event)
@@ -248,7 +266,7 @@ describe "PaneContainer", ->
describe "::onWillDestroyPaneItem() and ::onDidDestroyPaneItem", ->
it "invokes the given callbacks when an item will be destroyed on any pane", ->
container = new PaneContainer
container = new PaneContainer(params)
pane1 = container.getRoot()
item1 = new Object
item2 = new Object
@@ -275,7 +293,7 @@ describe "PaneContainer", ->
describe "::saveAll()", ->
it "saves all open pane items", ->
container = new PaneContainer
container = new PaneContainer(params)
pane1 = container.getRoot()
pane2 = pane1.splitRight()

View File

@@ -4,8 +4,10 @@ describe "PaneElement", ->
[paneElement, container, pane] = []
beforeEach ->
container = new PaneContainer
pane = container.getRoot()
spyOn(atom, "open")
container = new PaneContainer(config: atom.config, confirm: atom.confirm.bind(atom))
pane = container.getActivePane()
paneElement = atom.views.getView(pane)
describe "when the pane's active status changes", ->
@@ -183,7 +185,6 @@ describe "PaneElement", ->
describe "when a file is dragged to the pane", ->
it "opens it", ->
spyOn(atom, "open")
event = buildDragEvent("drop", [{path: "/fake1"}, {path: "/fake2"}])
paneElement.dispatchEvent(event)
expect(atom.open.callCount).toBe 1
@@ -191,7 +192,6 @@ describe "PaneElement", ->
describe "when a non-file is dragged to the pane", ->
it "does nothing", ->
spyOn(atom, "open")
event = buildDragEvent("drop", [])
paneElement.dispatchEvent(event)
expect(atom.open).not.toHaveBeenCalled()

View File

@@ -1,10 +1,11 @@
{extend} = require 'underscore-plus'
{Emitter} = require 'event-kit'
Pane = require '../src/pane'
PaneAxis = require '../src/pane-axis'
PaneContainer = require '../src/pane-container'
describe "Pane", ->
deserializerDisposable = null
[confirm, showSaveDialog, deserializerDisposable] = []
class Item
@deserialize: ({name, uri}) -> new this(name, uri)
@@ -19,18 +20,28 @@ describe "Pane", ->
isDestroyed: -> @destroyed
beforeEach ->
confirm = spyOn(atom.applicationDelegate, 'confirm')
showSaveDialog = spyOn(atom.applicationDelegate, 'showSaveDialog')
deserializerDisposable = atom.deserializers.add(Item)
afterEach ->
deserializerDisposable.dispose()
paneParams = (params) ->
extend({
applicationDelegate: atom.applicationDelegate,
config: atom.config,
deserializerManager: atom.deserializers,
notificationManager: atom.notifications
}, params)
describe "construction", ->
it "sets the active item to the first item", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
it "compacts the items array", ->
pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")])
pane = new Pane(paneParams(items: [undefined, new Item("A"), null, new Item("B")]))
expect(pane.getItems().length).toBe 2
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
@@ -38,8 +49,8 @@ describe "Pane", ->
[container, pane1, pane2] = []
beforeEach ->
container = new PaneContainer(root: new Pane)
container.getRoot().splitRight()
container = new PaneContainer(config: atom.config, applicationDelegate: atom.applicationDelegate)
container.getActivePane().splitRight()
[pane1, pane2] = container.getPanes()
it "changes the active pane on the container", ->
@@ -76,14 +87,14 @@ describe "Pane", ->
describe "::addItem(item, index)", ->
it "adds the item at the given index", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
[item1, item2] = pane.getItems()
item3 = new Item("C")
pane.addItem(item3, 1)
expect(pane.getItems()).toEqual [item1, item3, item2]
it "adds the item after the active item if no index is provided", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.activateItem(item2)
item4 = new Item("D")
@@ -91,13 +102,13 @@ describe "Pane", ->
expect(pane.getItems()).toEqual [item1, item2, item4, item3]
it "sets the active item after adding the first item", ->
pane = new Pane
pane = new Pane(paneParams())
item = new Item("A")
pane.addItem(item)
expect(pane.getActiveItem()).toBe item
it "invokes ::onDidAddItem() observers", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
events = []
pane.onDidAddItem (event) -> events.push(event)
@@ -107,13 +118,14 @@ describe "Pane", ->
it "throws an exception if the item is already present on a pane", ->
item = new Item("A")
pane1 = new Pane(items: [item])
container = new PaneContainer(root: pane1)
container = new PaneContainer(config: atom.config, applicationDelegate: atom.applicationDelegate)
pane1 = container.getActivePane()
pane1.addItem(item)
pane2 = pane1.splitRight()
expect(-> pane2.addItem(item)).toThrow()
it "throws an exception if the item isn't an object", ->
pane = new Pane(items: [])
pane = new Pane(paneParams(items: []))
expect(-> pane.addItem(null)).toThrow()
expect(-> pane.addItem('foo')).toThrow()
expect(-> pane.addItem(1)).toThrow()
@@ -122,7 +134,7 @@ describe "Pane", ->
pane = null
beforeEach ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
it "changes the active item to the current item", ->
expect(pane.getActiveItem()).toBe pane.itemAtIndex(0)
@@ -143,7 +155,7 @@ describe "Pane", ->
describe "::activateNextItem() and ::activatePreviousItem()", ->
it "sets the active item to the next/previous item, looping around at either end", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
expect(pane.getActiveItem()).toBe item1
@@ -158,7 +170,7 @@ describe "Pane", ->
describe "::moveItemRight() and ::moveItemLeft()", ->
it "moves the active item to the right and left, without looping around at either end", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.activateItemAtIndex(0)
@@ -176,7 +188,7 @@ describe "Pane", ->
describe "::activateItemAtIndex(index)", ->
it "activates the item at the given index", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.activateItemAtIndex(2)
expect(pane.getActiveItem()).toBe item3
@@ -195,7 +207,7 @@ describe "Pane", ->
[pane, item1, item2, item3] = []
beforeEach ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
it "removes the item from the items list and destroyes it", ->
@@ -259,7 +271,7 @@ describe "Pane", ->
describe "when the item has a uri", ->
it "saves the item before destroying it", ->
itemURI = "test"
spyOn(atom, 'confirm').andReturn(0)
confirm.andReturn(0)
pane.destroyItem(item1)
expect(item1.save).toHaveBeenCalled()
@@ -270,18 +282,18 @@ describe "Pane", ->
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
itemURI = null
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
spyOn(atom, 'confirm').andReturn(0)
showSaveDialog.andReturn("/selected/path")
confirm.andReturn(0)
pane.destroyItem(item1)
expect(atom.showSaveDialogSync).toHaveBeenCalled()
expect(showSaveDialog).toHaveBeenCalled()
expect(item1.saveAs).toHaveBeenCalledWith("/selected/path")
expect(item1 in pane.getItems()).toBe false
expect(item1.isDestroyed()).toBe true
describe "if the [Don't Save] option is selected", ->
it "removes and destroys the item without saving it", ->
spyOn(atom, 'confirm').andReturn(2)
confirm.andReturn(2)
pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
@@ -290,7 +302,7 @@ describe "Pane", ->
describe "if the [Cancel] option is selected", ->
it "does not save, remove, or destroy the item", ->
spyOn(atom, 'confirm').andReturn(1)
confirm.andReturn(1)
pane.destroyItem(item1)
expect(item1.save).not.toHaveBeenCalled()
@@ -315,19 +327,19 @@ describe "Pane", ->
describe "::destroyActiveItem()", ->
it "destroys the active item", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
activeItem = pane.getActiveItem()
pane.destroyActiveItem()
expect(activeItem.isDestroyed()).toBe true
expect(activeItem in pane.getItems()).toBe false
it "does not throw an exception if there are no more items", ->
pane = new Pane
pane = new Pane(paneParams())
pane.destroyActiveItem()
describe "::destroyItems()", ->
it "destroys all items", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.destroyItems()
expect(item1.isDestroyed()).toBe true
@@ -337,7 +349,7 @@ describe "Pane", ->
describe "::observeItems()", ->
it "invokes the observer with all current and future items", ->
pane = new Pane(items: [new Item, new Item])
pane = new Pane(paneParams(items: [new Item, new Item]))
[item1, item2] = pane.getItems()
observed = []
@@ -350,14 +362,14 @@ describe "Pane", ->
describe "when an item emits a destroyed event", ->
it "removes it from the list of items", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.itemAtIndex(1).destroy()
expect(pane.getItems()).toEqual [item1, item3]
describe "::destroyInactiveItems()", ->
it "destroys all items but the active item", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
[item1, item2, item3] = pane.getItems()
pane.activateItem(item2)
pane.destroyInactiveItems()
@@ -367,8 +379,8 @@ describe "Pane", ->
pane = null
beforeEach ->
pane = new Pane(items: [new Item("A")])
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
pane = new Pane(paneParams(items: [new Item("A")]))
showSaveDialog.andReturn('/selected/path')
describe "when the active item has a uri", ->
beforeEach ->
@@ -390,14 +402,14 @@ describe "Pane", ->
it "opens a save dialog and saves the current item as the selected path", ->
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
pane.saveActiveItem()
expect(atom.showSaveDialogSync).toHaveBeenCalled()
expect(showSaveDialog).toHaveBeenCalled()
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
describe "when the current item has no saveAs method", ->
it "does nothing", ->
expect(pane.getActiveItem().saveAs).toBeUndefined()
pane.saveActiveItem()
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
expect(showSaveDialog).not.toHaveBeenCalled()
describe "when the item's saveAs method throws a well-known IO error", ->
notificationSpy = null
@@ -422,22 +434,22 @@ describe "Pane", ->
pane = null
beforeEach ->
pane = new Pane(items: [new Item("A")])
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
pane = new Pane(paneParams(items: [new Item("A")]))
showSaveDialog.andReturn('/selected/path')
describe "when the current item has a saveAs method", ->
it "opens the save dialog and calls saveAs on the item with the selected path", ->
pane.getActiveItem().path = __filename
pane.getActiveItem().saveAs = jasmine.createSpy("saveAs")
pane.saveActiveItemAs()
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(defaultPath: __filename)
expect(showSaveDialog).toHaveBeenCalledWith(defaultPath: __filename)
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
describe "when the current item does not have a saveAs method", ->
it "does nothing", ->
expect(pane.getActiveItem().saveAs).toBeUndefined()
pane.saveActiveItemAs()
expect(atom.showSaveDialogSync).not.toHaveBeenCalled()
expect(showSaveDialog).not.toHaveBeenCalled()
describe "when the item's saveAs method throws a well-known IO error", ->
notificationSpy = null
@@ -460,7 +472,7 @@ describe "Pane", ->
describe "::itemForURI(uri)", ->
it "returns the item for which a call to .getURI() returns the given uri", ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]))
[item1, item2, item3] = pane.getItems()
item1.uri = "a"
item2.uri = "b"
@@ -472,7 +484,7 @@ describe "Pane", ->
[pane, item1, item2, item3, item4] = []
beforeEach ->
pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]))
[item1, item2, item3, item4] = pane.getItems()
it "moves the item to the given index and invokes ::onDidMoveItem observers", ->
@@ -501,8 +513,9 @@ describe "Pane", ->
[item1, item2, item3, item4, item5] = []
beforeEach ->
pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")])
container = new PaneContainer(root: pane1)
container = new PaneContainer(config: atom.config, confirm: confirm)
pane1 = container.getActivePane()
pane1.addItems([new Item("A"), new Item("B"), new Item("C")])
pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")])
[item1, item2, item3] = pane1.getItems()
[item4, item5] = pane2.getItems()
@@ -553,8 +566,9 @@ describe "Pane", ->
[pane1, container] = []
beforeEach ->
pane1 = new Pane(items: [new Item("A")])
container = new PaneContainer(root: pane1)
container = new PaneContainer(config: atom.config, confirm: confirm, deserializerManager: atom.deserializers)
pane1 = container.getActivePane()
pane1.addItem(new Item("A"))
describe "::splitLeft(params)", ->
describe "when the parent is the container root", ->
@@ -652,32 +666,32 @@ describe "Pane", ->
describe "::close()", ->
it "prompts to save unsaved items before destroying the pane", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
[item1, item2] = pane.getItems()
item1.shouldPromptToSave = -> true
item1.getURI = -> "/test/path"
item1.save = jasmine.createSpy("save")
spyOn(atom, 'confirm').andReturn(0)
confirm.andReturn(0)
pane.close()
expect(atom.confirm).toHaveBeenCalled()
expect(confirm).toHaveBeenCalled()
expect(item1.save).toHaveBeenCalled()
expect(pane.isDestroyed()).toBe true
it "does not destroy the pane if cancel is called", ->
pane = new Pane(items: [new Item("A"), new Item("B")])
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
[item1, item2] = pane.getItems()
item1.shouldPromptToSave = -> true
item1.getURI = -> "/test/path"
item1.save = jasmine.createSpy("save")
spyOn(atom, 'confirm').andReturn(1)
confirm.andReturn(1)
pane.close()
expect(atom.confirm).toHaveBeenCalled()
expect(confirm).toHaveBeenCalled()
expect(item1.save).not.toHaveBeenCalled()
expect(pane.isDestroyed()).toBe false
@@ -685,7 +699,7 @@ describe "Pane", ->
[container, pane1, pane2] = []
beforeEach ->
container = new PaneContainer
container = new PaneContainer(config: atom.config, confirm: confirm)
pane1 = container.root
pane1.addItems([new Item("A"), new Item("B")])
pane2 = pane1.splitRight()
@@ -732,18 +746,18 @@ describe "Pane", ->
pane = null
beforeEach ->
params =
pane = new Pane(paneParams(
items: [new Item("A", "a"), new Item("B", "b"), new Item("C", "c")]
flexScale: 2
pane = new Pane(params)
))
it "can serialize and deserialize the pane and all its items", ->
newPane = Pane.deserialize(pane.serialize())
newPane = Pane.deserialize(pane.serialize(), atom)
expect(newPane.getItems()).toEqual pane.getItems()
it "restores the active item on deserialization", ->
pane.activateItemAtIndex(1)
newPane = Pane.deserialize(pane.serialize())
newPane = Pane.deserialize(pane.serialize(), atom)
expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1)
it "does not include items that cannot be deserialized", ->
@@ -751,11 +765,11 @@ describe "Pane", ->
unserializable = {}
pane.activateItem(unserializable)
newPane = Pane.deserialize(pane.serialize())
newPane = Pane.deserialize(pane.serialize(), atom)
expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0)
expect(newPane.getItems().length).toBe pane.getItems().length - 1
it "includes the pane's focus state in the serialized state", ->
pane.focus()
newPane = Pane.deserialize(pane.serialize())
newPane = Pane.deserialize(pane.serialize(), atom)
expect(newPane.focused).toBe true

View File

@@ -20,10 +20,6 @@ describe "PanelContainerElement", ->
beforeEach ->
jasmineContent = document.body.querySelector('#jasmine-content')
atom.views.addViewProvider Panel, (model) ->
new PanelElement().initialize(model)
atom.views.addViewProvider PanelContainer, (model) ->
new PaneContainerElement().initialize(model)
atom.views.addViewProvider TestPanelContainerItem, (model) ->
new TestPanelContainerItemElement().initialize(model)

View File

@@ -18,8 +18,8 @@ describe "PanelElement", ->
beforeEach ->
jasmineContent = document.body.querySelector('#jasmine-content')
atom.views.addViewProvider Panel, (model) ->
new PanelElement().initialize(model)
atom.views.addViewProvider Panel, (model, env) ->
new PanelElement().initialize(model, env)
atom.views.addViewProvider TestPanelItem, (model) ->
new TestPanelItemElement().initialize(model)

View File

@@ -16,44 +16,6 @@ describe "Project", ->
# Wait for project's service consumers to be asynchronously added
waits(1)
describe "when a new repository-provider is added", ->
it "uses it to create repositories for any directories that need one", ->
projectPath = temp.mkdirSync('atom-project')
atom.project.setPaths([projectPath])
expect(atom.project.getRepositories()).toEqual [null]
expect(atom.project.repositoryProviders.length).toEqual 1
dummyRepository = {destroy: -> null}
atom.packages.serviceHub.provide("atom.repository-provider", "0.1.0", {
repositoryForDirectory: (directory) -> Promise.resolve(dummyRepository)
repositoryForDirectorySync: (directory) -> dummyRepository
})
repository = null
waitsFor "repository to be updated", ->
repository = atom.project.getRepositories()[0]
runs ->
expect(repository).toBe dummyRepository
it "does not create any new repositories if every directory has a repository", ->
repositories = atom.project.getRepositories()
expect(repositories.length).toEqual 1
[repository] = repositories
expect(repository).toBeTruthy()
# Register a new RepositoryProvider.
dummyRepository = destroy: ->
repositoryProvider =
repositoryForDirectory: (directory) -> Promise.resolve(dummyRepository)
repositoryForDirectorySync: (directory) -> dummyRepository
atom.packages.serviceHub.provide(
"atom.repository-provider", "0.1.0", repositoryProvider)
expect(atom.project.getRepositories()).toBe repositories
describe "serialization", ->
deserializedProject = null
@@ -66,16 +28,19 @@ describe "Project", ->
runs ->
expect(atom.project.getBuffers().length).toBe 1
deserializedProject = Project.deserialize(atom.project.serialize())
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
expect(deserializedProject.getBuffers().length).toBe 0
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
waitsForPromise ->
atom.project.open('a')
atom.workspace.open('a')
runs ->
expect(atom.project.getBuffers().length).toBe 1
deserializedProject = Project.deserialize(atom.project.serialize())
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
expect(deserializedProject.getBuffers().length).toBe 1
deserializedProject.getBuffers()[0].destroy()
@@ -86,12 +51,13 @@ describe "Project", ->
pathToOpen = path.join(temp.mkdirSync(), 'file.txt')
waitsForPromise ->
atom.project.open(pathToOpen)
atom.workspace.open(pathToOpen)
runs ->
expect(atom.project.getBuffers().length).toBe 1
fs.mkdirSync(pathToOpen)
deserializedProject = Project.deserialize(atom.project.serialize())
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
expect(deserializedProject.getBuffers().length).toBe 0
it "does not deserialize buffers when their path is inaccessible", ->
@@ -99,12 +65,13 @@ describe "Project", ->
fs.writeFileSync(pathToOpen, '')
waitsForPromise ->
atom.project.open(pathToOpen)
atom.workspace.open(pathToOpen)
runs ->
expect(atom.project.getBuffers().length).toBe 1
fs.chmodSync(pathToOpen, '000')
deserializedProject = Project.deserialize(atom.project.serialize())
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
expect(deserializedProject.getBuffers().length).toBe 0
describe "when an editor is saved and the project has no path", ->
@@ -115,7 +82,7 @@ describe "Project", ->
editor = null
waitsForPromise ->
atom.project.open().then (o) -> editor = o
atom.workspace.open().then (o) -> editor = o
runs ->
editor.saveAs(tempFile)
@@ -125,7 +92,7 @@ describe "Project", ->
editor = null
beforeEach ->
waitsForPromise ->
atom.project.open(require.resolve('./fixtures/dir/a')).then (o) -> editor = o
atom.workspace.open(require.resolve('./fixtures/dir/a')).then (o) -> editor = o
it "creates a warning notification", ->
atom.notifications.onDidAddNotification noteSpy = jasmine.createSpy()
@@ -144,6 +111,106 @@ describe "Project", ->
expect(notification.getMessage()).toContain '`resurrect`'
expect(notification.getMessage()).toContain 'fixtures/dir/a'
describe "when a custom repository-provider service is provided", ->
[fakeRepositoryProvider, fakeRepository] = []
beforeEach ->
fakeRepository = {destroy: -> null}
fakeRepositoryProvider = {
repositoryForDirectory: (directory) -> Promise.resolve(fakeRepository)
repositoryForDirectorySync: (directory) -> fakeRepository
}
it "uses it to create repositories for any directories that need one", ->
projectPath = temp.mkdirSync('atom-project')
atom.project.setPaths([projectPath])
expect(atom.project.getRepositories()).toEqual [null]
atom.packages.serviceHub.provide("atom.repository-provider", "0.1.0", fakeRepositoryProvider)
waitsFor -> atom.project.repositoryProviders.length > 1
runs -> atom.project.getRepositories()[0] is fakeRepository
it "does not create any new repositories if every directory has a repository", ->
repositories = atom.project.getRepositories()
expect(repositories.length).toEqual 1
expect(repositories[0]).toBeTruthy()
atom.packages.serviceHub.provide("atom.repository-provider", "0.1.0", fakeRepositoryProvider)
waitsFor -> atom.project.repositoryProviders.length > 1
runs -> expect(atom.project.getRepositories()).toBe repositories
it "stops using it to create repositories when the service is removed", ->
atom.project.setPaths([])
disposable = atom.packages.serviceHub.provide("atom.repository-provider", "0.1.0", fakeRepositoryProvider)
waitsFor -> atom.project.repositoryProviders.length > 1
runs ->
disposable.dispose()
atom.project.addPath(temp.mkdirSync('atom-project'))
expect(atom.project.getRepositories()).toEqual [null]
describe "when a custom directory-provider service is provided", ->
class DummyDirectory
constructor: (@path) ->
getPath: -> @path
getFile: -> {existsSync: -> false}
getSubdirectory: -> {existsSync: -> false}
isRoot: -> true
existsSync: -> @path.endsWith('does-exist')
contains: (filePath) -> filePath.startsWith(@path)
serviceDisposable = null
beforeEach ->
serviceDisposable = atom.packages.serviceHub.provide("atom.directory-provider", "0.1.0", {
directoryForURISync: (uri) ->
if uri.startsWith("ssh://")
new DummyDirectory(uri)
else
null
})
waitsFor ->
atom.project.directoryProviders.length > 0
it "uses the provider's custom directories for any paths that it handles", ->
localPath = temp.mkdirSync('local-path')
remotePath = "ssh://foreign-directory:8080/does-exist"
atom.project.setPaths([localPath, remotePath])
directories = atom.project.getDirectories()
expect(directories[0].getPath()).toBe localPath
expect(directories[0] instanceof Directory).toBe true
expect(directories[1].getPath()).toBe remotePath
expect(directories[1] instanceof DummyDirectory).toBe true
# It does not add new remote paths if their directories do not exist
# and they are contained by existing remote paths.
childRemotePath = remotePath + "/subdirectory/that/does-not-exist"
atom.project.addPath(childRemotePath)
expect(atom.project.getDirectories().length).toBe 2
# It does add new remote paths if their directories exist.
childRemotePath = remotePath + "/subdirectory/that/does-exist"
atom.project.addPath(childRemotePath)
directories = atom.project.getDirectories()
expect(directories[2].getPath()).toBe childRemotePath
expect(directories[2] instanceof DummyDirectory).toBe true
# It does add new remote paths to be added if they are not contained by
# previous remote paths.
otherRemotePath = "ssh://other-foreign-directory:8080/"
atom.project.addPath(otherRemotePath)
directories = atom.project.getDirectories()
expect(directories[3].getPath()).toBe otherRemotePath
expect(directories[3] instanceof DummyDirectory).toBe true
it "stops using the provider when the service is removed", ->
serviceDisposable.dispose()
atom.project.setPaths(["ssh://foreign-directory:8080/does-exist"])
expect(atom.project.getDirectories()[0] instanceof Directory).toBe true
describe ".open(path)", ->
[absolutePath, newBufferHandler] = []
@@ -156,7 +223,7 @@ describe "Project", ->
it "returns a new edit session for the given path and emits 'buffer-created'", ->
editor = null
waitsForPromise ->
atom.project.open(absolutePath).then (o) -> editor = o
atom.workspace.open(absolutePath).then (o) -> editor = o
runs ->
expect(editor.buffer.getPath()).toBe absolutePath
@@ -166,7 +233,7 @@ describe "Project", ->
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", ->
editor = null
waitsForPromise ->
atom.project.open(absolutePath).then (o) -> editor = o
atom.workspace.open(absolutePath).then (o) -> editor = o
runs ->
expect(editor.buffer.getPath()).toBe absolutePath
@@ -177,17 +244,17 @@ describe "Project", ->
editor = null
waitsForPromise ->
atom.project.open(absolutePath).then (o) -> editor = o
atom.workspace.open(absolutePath).then (o) -> editor = o
runs ->
newBufferHandler.reset()
waitsForPromise ->
atom.project.open(absolutePath).then ({buffer}) ->
atom.workspace.open(absolutePath).then ({buffer}) ->
expect(buffer).toBe editor.buffer
waitsForPromise ->
atom.project.open('a').then ({buffer}) ->
atom.workspace.open('a').then ({buffer}) ->
expect(buffer).toBe editor.buffer
expect(newBufferHandler).not.toHaveBeenCalled()
@@ -195,7 +262,7 @@ describe "Project", ->
it "returns a new edit session and emits 'buffer-created'", ->
editor = null
waitsForPromise ->
atom.project.open().then (o) -> editor = o
atom.workspace.open().then (o) -> editor = o
runs ->
expect(editor.buffer.getPath()).toBeUndefined()
@@ -305,68 +372,6 @@ describe "Project", ->
expect(directories.length).toBe 1
expect(directories[0].getPath()).toBe path.normalize(nonLocalFsDirectory)
describe "when a custom directory provider has been added", ->
describe "when custom provider handles the given path", ->
it "creates a directory using that provider", ->
class DummyDirectory
constructor: (@path) ->
getPath: -> @path
getFile: -> {existsSync: -> false}
getSubdirectory: -> {existsSync: -> false}
isRoot: -> true
existsSync: -> /does-exist/.test(@path)
off: ->
contains: (filePath) -> filePath.startsWith(@path)
atom.packages.serviceHub.provide("atom.directory-provider", "0.1.0", {
directoryForURISync: (uri) ->
if uri.startsWith("ssh://")
new DummyDirectory(uri)
else
null
})
localPath = temp.mkdirSync('local-path')
remotePath = "ssh://foreign-directory:8080/exists"
atom.project.setPaths([localPath, remotePath])
directories = atom.project.getDirectories()
expect(directories[0].getPath()).toBe localPath
expect(directories[0] instanceof Directory).toBe true
expect(directories[1].getPath()).toBe remotePath
expect(directories[1] instanceof DummyDirectory).toBe true
# Make sure that DummyDirectory.contains() is honored.
remotePathSubdirectory = remotePath + "a/subdirectory"
atom.project.addPath(remotePathSubdirectory)
expect(atom.project.getDirectories().length).toBe 2
# Make sure that a new DummyDirectory that is not contained by the first
# DummyDirectory can be added.
otherRemotePath = "ssh://other-foreign-directory:8080/"
atom.project.addPath(otherRemotePath)
newDirectories = atom.project.getDirectories()
expect(newDirectories.length).toBe 3
otherDummyDirectory = newDirectories[2]
expect(otherDummyDirectory.getPath()).toBe otherRemotePath
expect(otherDummyDirectory instanceof DummyDirectory).toBe true
describe "when a custom provider does not handle the path", ->
it "creates a local directory for the path", ->
directoryProvider =
directoryForURISync: (uri) -> null
directoryForURI: (uri) -> throw new Error("This should not be called.")
atom.packages.serviceHub.provide(
"atom.directory-provider", "0.1.0", directoryProvider)
tmp = temp.mkdirSync()
atom.project.setPaths([tmp])
directories = atom.project.getDirectories()
expect(directories.length).toBe 1
expect(directories[0].getPath()).toBe tmp
describe ".addPath(path)", ->
it "calls callbacks registered with ::onDidChangePaths", ->
onDidChangePathsSpy = jasmine.createSpy('onDidChangePaths spy')

View File

@@ -15,7 +15,7 @@ describe "TextEditor", ->
it "properly renders soft-wrapped lines when randomly mutated", ->
times 10, (i) ->
buffer = new TextBuffer
editor = new TextEditor({buffer})
editor = atom.workspace.buildTextEditor({buffer})
editor.setEditorWidthInChars(80)
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
steps = []
@@ -80,7 +80,7 @@ describe "TextEditor", ->
text
getReferenceScreenLines = ->
referenceEditor = new TextEditor({})
referenceEditor = atom.workspace.buildTextEditor()
referenceEditor.setEditorWidthInChars(80)
referenceEditor.setText(editor.getText())
referenceEditor.setSoftWrapped(editor.isSoftWrapped())

View File

@@ -0,0 +1 @@
undefined

1
spec/sample.js Normal file
View File

@@ -0,0 +1 @@
undefined

View File

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

View File

@@ -1,30 +0,0 @@
# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
path = require 'path'
try
require '../src/window'
Atom = require '../src/atom'
window.atom = Atom.loadOrCreate('spec')
# Show window synchronously so a focusout doesn't fire on input elements
# that are focused in the very first spec run.
atom.getCurrentWindow().show() unless atom.getLoadSettings().exitWhenDone
{runSpecSuite} = require './jasmine-helper'
# Add 'exports' to module search path.
exportsPath = path.join(atom.getLoadSettings().resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
# Still set NODE_PATH since tasks may need it.
process.env.NODE_PATH = exportsPath
document.title = "Spec Suite"
runSpecSuite './spec-suite', atom.getLoadSettings().logFile
catch error
if atom?.getLoadSettings().exitWhenDone
console.error(error.stack ? error)
atom.exit(1)
else
throw error

View File

@@ -1,6 +1,4 @@
require '../src/window'
atom.initialize()
atom.restoreWindowDimensions()
require 'jasmine-json'
require '../vendor/jasmine-jquery'
@@ -8,45 +6,27 @@ path = require 'path'
_ = require 'underscore-plus'
fs = require 'fs-plus'
Grim = require 'grim'
KeymapManager = require '../src/keymap-extensions'
pathwatcher = require 'pathwatcher'
FindParentDir = require 'find-parent-dir'
Config = require '../src/config'
{Point} = require 'text-buffer'
Project = require '../src/project'
Workspace = require '../src/workspace'
ServiceHub = require 'service-hub'
TextEditor = require '../src/text-editor'
TextEditorElement = require '../src/text-editor-element'
TokenizedBuffer = require '../src/tokenized-buffer'
TextEditorComponent = require '../src/text-editor-component'
pathwatcher = require 'pathwatcher'
clipboard = require '../src/safe-clipboard'
atom.themes.loadBaseStylesheets()
atom.themes.requireStylesheet '../static/jasmine'
atom.themes.initialLoadComplete = true
jasmineStyle = document.createElement('style')
jasmineStyle.textContent = atom.themes.loadStylesheet(atom.themes.resolveStylesheet('../static/jasmine'))
document.head.appendChild(jasmineStyle)
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
atom.keymaps.loadBundledKeymaps()
keyBindingsToRestore = atom.keymaps.getKeyBindings()
commandsToRestore = atom.commands.getSnapshot()
styleElementsToRestore = atom.styles.getSnapshot()
window.addEventListener 'core:close', -> window.close()
window.addEventListener 'beforeunload', ->
atom.storeWindowDimensions()
atom.saveSync()
document.querySelector('html').style.overflow = 'auto'
document.body.style.overflow = 'auto'
# Allow document.title to be assigned in specs without screwing up spec window title
documentTitle = null
Object.defineProperty document, 'title',
get: -> documentTitle
set: (title) -> documentTitle = title
Set.prototype.jasmineToString = ->
result = "Set {"
first = true
@@ -73,46 +53,27 @@ if process.env.CI
else
jasmine.getEnv().defaultTimeoutInterval = 5000
specPackageName = null
specPackagePath = null
specProjectPath = null
isCoreSpec = false
{resourcePath, testPaths} = atom.getLoadSettings()
{specDirectory, resourcePath} = atom.getLoadSettings()
if specPackagePath = FindParentDir.sync(testPaths[0], 'package.json')
packageMetadata = require(path.join(specPackagePath, 'package.json'))
specPackageName = packageMetadata.name
if specDirectory
specPackagePath = path.resolve(specDirectory, '..')
try
specPackageName = JSON.parse(fs.readFileSync(path.join(specPackagePath, 'package.json')))?.name
if specDirectory = FindParentDir.sync(testPaths[0], 'fixtures')
specProjectPath = path.join(specDirectory, 'fixtures')
isCoreSpec = specDirectory is fs.realpathSync(__dirname)
else
specProjectPath = path.join(__dirname, 'fixtures')
beforeEach ->
documentTitle = null
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
atom.packages.serviceHub = new ServiceHub
atom.project = new Project(paths: [projectPath])
atom.workspace = new Workspace()
atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
atom.commands.restoreSnapshot(commandsToRestore)
atom.styles.restoreSnapshot(styleElementsToRestore)
atom.views.clearDocumentRequests()
atom.workspaceParentSelectorctor = '#jasmine-content'
atom.project.setPaths([specProjectPath])
window.resetTimeouts()
spyOn(_._, "now").andCallFake -> window.now
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
atom.packages.packageStates = {}
serializedWindowState = null
spyOn(atom, 'saveSync')
atom.grammars.clearGrammarOverrides()
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
if specPackageName and packageName is specPackageName
resolvePackagePath(specPackagePath)
@@ -123,28 +84,20 @@ beforeEach ->
# prevent specs from modifying Atom's menus
spyOn(atom.menu, 'sendToBrowserProcess')
# reset config before each spec; don't load or save from/to `config.json`
spyOn(Config::, 'load')
spyOn(Config::, 'save')
config = new Config({resourcePath, configDirPath: atom.getConfigDirPath()})
atom.config = config
atom.loadConfig()
config.set "core.destroyEmptyPanes", false
config.set "editor.fontFamily", "Courier"
config.set "editor.fontSize", 16
config.set "editor.autoIndent", false
config.set "core.disabledPackages", ["package-that-throws-an-exception",
# reset config before each spec
atom.config.set "core.destroyEmptyPanes", false
atom.config.set "editor.fontFamily", "Courier"
atom.config.set "editor.fontSize", 16
atom.config.set "editor.autoIndent", false
atom.config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"]
config.set "editor.useShadowDOM", true
atom.config.set "editor.useShadowDOM", true
advanceClock(1000)
window.setTimeout.reset()
config.load.reset()
config.save.reset()
# make editor display updates synchronous
TextEditorElement::setUpdatedSynchronously(true)
spyOn(atom, "setRepresentedFilename")
spyOn(pathwatcher.File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
spyOn(TextEditor.prototype, "shouldPromptToSave").andReturn false
@@ -159,25 +112,10 @@ beforeEach ->
addCustomMatchers(this)
afterEach ->
atom.packages.deactivatePackages()
atom.menu.template = []
atom.contextMenu.clear()
atom.notifications.clear()
atom.workspace?.destroy()
atom.workspace = null
delete atom.state.workspace
atom.project?.destroy()
atom.project = null
atom.themes.removeStylesheet('global-editor-styles')
delete atom.state.packageStates
atom.reset()
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
jasmine.unspy(atom, 'saveSync')
ensureNoPathSubscriptions()
waits(0) # yield to ui thread to make screen update more frequently

View File

@@ -1,55 +0,0 @@
_ = require 'underscore-plus'
fs = require 'fs-plus'
path = require 'path'
require './spec-helper'
requireSpecs = (specDirectory, specType) ->
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.(coffee|js)$/.test specFilePath
require specFilePath
# Set spec directory on spec for setting up the project in spec-helper
setSpecDirectory(specDirectory)
setSpecField = (name, value) ->
specs = jasmine.getEnv().currentRunner().specs()
return if specs.length is 0
for index in [specs.length-1..0]
break if specs[index][name]?
specs[index][name] = value
setSpecType = (specType) ->
setSpecField('specType', specType)
setSpecDirectory = (specDirectory) ->
setSpecField('specDirectory', specDirectory)
runAllSpecs = ->
{resourcePath} = atom.getLoadSettings()
requireSpecs(path.join(resourcePath, 'spec'))
setSpecType('core')
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
packagePaths = atom.packages.getAvailablePackageNames().map (packageName) ->
atom.packages.resolvePackagePath(packageName)
packagePaths = _.groupBy packagePaths, (packagePath) ->
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
'fixtures'
else if packagePath.indexOf("#{resourcePath}#{path.sep}") is 0
'bundled'
else
'user'
# Run bundled package specs
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
setSpecType('bundled')
# Run user package specs
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
setSpecType('user')
if specDirectory = atom.getLoadSettings().specDirectory
requireSpecs(specDirectory)
setSpecType('user')
else
runAllSpecs()

View File

@@ -4,7 +4,7 @@ describe "StyleManager", ->
[manager, addEvents, removeEvents, updateEvents] = []
beforeEach ->
manager = new StyleManager
manager = new StyleManager(configDirPath: atom.getConfigDirPath())
addEvents = []
removeEvents = []
updateEvents = []

View File

@@ -6,6 +6,7 @@ describe "StylesElement", ->
beforeEach ->
element = new StylesElement
element.initialize(atom.styles)
document.querySelector('#jasmine-content').appendChild(element)
addedStyleElements = []
removedStyleElements = []
@@ -99,8 +100,8 @@ describe "StylesElement", ->
it "defers selector upgrade until the element is attached", ->
element = new StylesElement
element.initialize(atom.styles)
element.setAttribute('context', 'atom-text-editor')
element.initialize()
atom.styles.addStyleSheet ".editor {background: black;}", context: 'atom-text-editor'
expect(element.firstChild.sheet).toBeNull()

View File

@@ -27,7 +27,7 @@ describe "TextEditorComponent", ->
fn()
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
contentNode = document.querySelector('#jasmine-content')
@@ -35,7 +35,7 @@ describe "TextEditorComponent", ->
wrapperNode = new TextEditorElement()
wrapperNode.tileSize = tileSize
wrapperNode.initialize(editor)
wrapperNode.initialize(editor, atom)
wrapperNode.setUpdatedSynchronously(false)
jasmine.attachToDOM(wrapperNode)
@@ -54,6 +54,10 @@ describe "TextEditorComponent", ->
component.measureDimensions()
nextAnimationFrame()
# Mutating the DOM in the previous frame causes a document poll; clear it here
waits 0
runs -> nextAnimationFrame()
afterEach ->
contentNode.style.width = ''
@@ -2914,7 +2918,7 @@ describe "TextEditorComponent", ->
wrapperNode = new TextEditorElement()
wrapperNode.tileSize = tileSize
wrapperNode.initialize(editor)
wrapperNode.initialize(editor, atom)
hiddenParent.appendChild(wrapperNode)
{component} = wrapperNode
@@ -3208,7 +3212,7 @@ describe "TextEditorComponent", ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
afterEach: ->
atom.packages.deactivatePackages()

View File

@@ -1,5 +1,6 @@
TextEditorElement = require '../src/text-editor-element'
TextEditor = require '../src/text-editor'
{Disposable} = require 'event-kit'
# The rest of text-editor-component-spec will be moved to this file when React
# is eliminated. This covers only concerns related to the wrapper element for now
@@ -33,7 +34,7 @@ describe "TextEditorElement", ->
describe "when the model is assigned", ->
it "adds the 'mini' attribute if .isMini() returns true on the model", ->
element = new TextEditorElement
model = new TextEditor(mini: true)
model = atom.workspace.buildTextEditor(mini: true)
element.setModel(model)
expect(element.hasAttribute('mini')).toBe true
@@ -67,7 +68,7 @@ describe "TextEditorElement", ->
describe "when the editor is detached from the DOM and then reattached", ->
it "does not render duplicate line numbers", ->
editor = new TextEditor
editor = atom.workspace.buildTextEditor()
editor.setText('1\n2\n3')
element = atom.views.getView(editor)
@@ -80,7 +81,7 @@ describe "TextEditorElement", ->
expect(element.shadowRoot.querySelectorAll('.line-number').length).toBe initialCount
it "does not render duplicate decorations in custom gutters", ->
editor = new TextEditor
editor = atom.workspace.buildTextEditor()
editor.setText('1\n2\n3')
editor.addGutter({name: 'test-gutter'})
marker = editor.markBufferRange([[0, 0], [2, 0]])
@@ -159,6 +160,7 @@ describe "TextEditorElement", ->
initialThemeLoadComplete
spyOn(atom.themes, 'onDidChangeActiveThemes').andCallFake (fn) ->
themeReloadCallback = fn
new Disposable
atom.config.set("editor.useShadowDOM", false)

View File

@@ -18,7 +18,7 @@ describe "TextEditorPresenter", ->
spyOn(window, "clearInterval").andCallFake window.fakeClearInterval
buffer = new TextBuffer(filePath: require.resolve('./fixtures/sample.js'))
editor = new TextEditor({buffer})
editor = atom.workspace.buildTextEditor({buffer})
waitsForPromise -> buffer.load()
afterEach ->
@@ -40,6 +40,7 @@ describe "TextEditorPresenter", ->
verticalScrollbarWidth: 10
scrollTop: 0
scrollLeft: 0
config: atom.config
presenter = new TextEditorPresenter(params)
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter))
@@ -2860,7 +2861,7 @@ describe "TextEditorPresenter", ->
performSetup = ->
buffer = new TextBuffer
editor = new TextEditor({buffer})
editor = atom.workspace.buildTextEditor({buffer})
editor.setEditorWidthInChars(80)
presenterParams =
model: editor

View File

@@ -12,7 +12,7 @@ describe "TextEditor", ->
beforeEach ->
waitsForPromise ->
atom.project.open('sample.js', autoIndent: false).then (o) -> editor = o
atom.workspace.open('sample.js', autoIndent: false).then (o) -> editor = o
runs ->
buffer = editor.buffer
@@ -27,11 +27,11 @@ describe "TextEditor", ->
editor1 = null
waitsForPromise ->
atom.project.open(pathToOpen).then (o) -> editor1 = o
atom.workspace.open(pathToOpen).then (o) -> editor1 = o
runs ->
fs.mkdirSync(pathToOpen)
expect(TextEditor.deserialize(editor1.serialize())).toBeUndefined()
expect(TextEditor.deserialize(editor1.serialize(), atom)).toBeUndefined()
it "restores selections and folds based on markers in the buffer", ->
editor.setSelectedBufferRange([[1, 2], [3, 4]])
@@ -39,7 +39,7 @@ describe "TextEditor", ->
editor.foldBufferRow(4)
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
editor2 = TextEditor.deserialize(editor.serialize())
editor2 = TextEditor.deserialize(editor.serialize(), atom)
expect(editor2.id).toBe editor.id
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
@@ -52,7 +52,7 @@ describe "TextEditor", ->
atom.config.set('editor.showInvisibles', true)
previousInvisibles = editor.tokenizedLineForScreenRow(0).invisibles
editor2 = TextEditor.deserialize(editor.serialize())
editor2 = TextEditor.deserialize(editor.serialize(), atom)
expect(previousInvisibles).toBeDefined()
expect(editor2.displayBuffer.tokenizedLineForScreenRow(0).invisibles).toEqual previousInvisibles
@@ -62,7 +62,7 @@ describe "TextEditor", ->
state = editor.serialize()
atom.config.set('editor.invisibles', eol: '?')
editor2 = TextEditor.deserialize(state)
editor2 = TextEditor.deserialize(state, atom)
expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?'
@@ -71,7 +71,7 @@ describe "TextEditor", ->
editor = null
waitsForPromise ->
atom.workspace.open('sample.js', largeFileMode: true).then (o) -> editor = o
atom.workspace.openTextFile('sample.js', largeFileMode: true).then (o) -> editor = o
runs ->
buffer = editor.getBuffer()
@@ -114,7 +114,7 @@ describe "TextEditor", ->
atom.config.set('core.fileEncoding', 'utf16le')
waitsForPromise ->
atom.workspace.open('a').then (o) -> editor1 = o
atom.workspace.open('dir/a').then (o) -> editor1 = o
runs ->
expect(editor1.getTabLength()).toBe 4
@@ -128,7 +128,7 @@ describe "TextEditor", ->
atom.config.set('core.fileEncoding', 'macroman')
waitsForPromise ->
atom.workspace.open('b').then (o) -> editor2 = o
atom.workspace.open('dir/b').then (o) -> editor2 = o
runs ->
expect(editor2.getTabLength()).toBe 8
@@ -144,13 +144,13 @@ describe "TextEditor", ->
atom.config.set('core.fileEncoding', 'macroman', scopeSelector: '.js')
waitsForPromise ->
atom.workspace.open('a').then (o) -> editor1 = o
atom.workspace.open('dir/a').then (o) -> editor1 = o
runs ->
expect(editor1.getEncoding()).toBe 'utf16le'
waitsForPromise ->
atom.workspace.open('test.js').then (o) -> editor2 = o
atom.workspace.open('sample-with-comments.js').then (o) -> editor2 = o
runs ->
expect(editor2.getEncoding()).toBe 'macroman'
@@ -1339,7 +1339,7 @@ describe "TextEditor", ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
it 'selects the correct surrounding word for the given scoped setting', ->
coffeeEditor.setCursorBufferPosition [0, 9] # in the middle of quicksort
@@ -1541,7 +1541,7 @@ describe "TextEditor", ->
it "takes atomic tokens into account", ->
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
atom.workspace.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
runs ->
editor.setSelectedBufferRange([[2, 1], [2, 3]])
@@ -1654,7 +1654,7 @@ describe "TextEditor", ->
it "takes atomic tokens into account", ->
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
atom.workspace.open('sample-with-tabs-and-leading-comment.coffee', autoIndent: false).then (o) -> editor = o
runs ->
editor.setSelectedBufferRange([[3, 1], [3, 2]])
@@ -1780,9 +1780,11 @@ describe "TextEditor", ->
it "does not share selections between different edit sessions for the same buffer", ->
editor2 = null
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor2 = o
atom.workspace.getActivePane().splitRight()
atom.workspace.open(editor.getPath()).then (o) -> editor2 = o
runs ->
expect(editor2.getText()).toBe(editor.getText())
editor.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
editor2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]])
expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges()
@@ -3685,7 +3687,7 @@ describe "TextEditor", ->
editor.destroy()
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
atom.workspace.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
runs ->
expect(editor.softTabs).toBe true
@@ -3758,7 +3760,7 @@ describe "TextEditor", ->
editor.destroy()
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
atom.workspace.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
runs ->
expect(editor.softTabs).toBe true
@@ -3813,7 +3815,7 @@ describe "TextEditor", ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
afterEach: ->
atom.packages.deactivatePackages()
@@ -4016,7 +4018,7 @@ describe "TextEditor", ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
runs ->
atom.config.set('editor.autoIndent', true, scopeSelector: '.source.js')
@@ -4166,7 +4168,8 @@ describe "TextEditor", ->
editor2 = null
waitsForPromise ->
atom.project.open('sample.js', autoIndent: false).then (o) -> editor2 = o
atom.workspace.getActivePane().splitRight()
atom.workspace.open('sample.js', autoIndent: false).then (o) -> editor2 = o
runs ->
expect(editor.shouldPromptToSave()).toBeFalsy()
@@ -4420,11 +4423,10 @@ describe "TextEditor", ->
describe '.get/setPlaceholderText()', ->
it 'can be created with placeholderText', ->
TextBuffer = require 'text-buffer'
newEditor = new TextEditor
buffer: new TextBuffer
newEditor = atom.workspace.buildTextEditor(
mini: true
placeholderText: 'yep'
)
expect(newEditor.getPlaceholderText()).toBe 'yep'
it 'models placeholderText and emits an event when changed', ->
@@ -4452,7 +4454,7 @@ describe "TextEditor", ->
describe "when there's no repository for the editor's file", ->
it "doesn't do anything", ->
editor = new TextEditor({})
editor = atom.workspace.buildTextEditor()
editor.setText("stuff")
editor.checkoutHeadRevision()

View File

@@ -24,7 +24,9 @@ describe "TokenIterator", ->
end x
x
""")
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
tokenizedBuffer.setGrammar(grammar)
tokenIterator = tokenizedBuffer.tokenizedLineForRow(1).getTokenIterator()

View File

@@ -27,7 +27,9 @@ describe "TokenizedBuffer", ->
describe "when the buffer is destroyed", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
startTokenizing(tokenizedBuffer)
it "stops tokenization", ->
@@ -39,7 +41,9 @@ describe "TokenizedBuffer", ->
describe "when the buffer contains soft-tabs", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
startTokenizing(tokenizedBuffer)
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
@@ -345,7 +349,9 @@ describe "TokenizedBuffer", ->
runs ->
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
startTokenizing(tokenizedBuffer)
afterEach ->
@@ -450,7 +456,9 @@ describe "TokenizedBuffer", ->
'abc\uD835\uDF97def'
//\uD835\uDF97xyz
"""
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
afterEach ->
@@ -487,7 +495,7 @@ describe "TokenizedBuffer", ->
tokenizedHandler = jasmine.createSpy("tokenized handler")
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
@@ -500,7 +508,7 @@ describe "TokenizedBuffer", ->
tokenizedHandler = jasmine.createSpy("tokenized handler")
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
@@ -518,7 +526,7 @@ describe "TokenizedBuffer", ->
tokenizedHandler = jasmine.createSpy("tokenized handler")
waitsForPromise ->
atom.project.open('coffee.coffee').then (o) -> editor = o
atom.workspace.open('coffee.coffee').then (o) -> editor = o
runs ->
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
@@ -544,7 +552,9 @@ describe "TokenizedBuffer", ->
runs ->
buffer = atom.project.bufferForPathSync()
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar('test.erb'))
fullyTokenize(tokenizedBuffer)
@@ -566,7 +576,9 @@ describe "TokenizedBuffer", ->
it "returns the correct token (regression)", ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
@@ -575,7 +587,10 @@ describe "TokenizedBuffer", ->
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars,
packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
describe "when the selector does not match the token at the position", ->
@@ -595,7 +610,9 @@ describe "TokenizedBuffer", ->
it "updates the tab length of the tokenized lines", ->
buffer = atom.project.bufferForPathSync('sample.js')
buffer.setText('\ttest')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
atom.config.set('editor.tabLength', 6)
@@ -604,7 +621,9 @@ describe "TokenizedBuffer", ->
it "does not allow the tab length to be less than 1", ->
buffer = atom.project.bufferForPathSync('sample.js')
buffer.setText('\ttest')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
atom.config.set('editor.tabLength', 1)
@@ -617,7 +636,9 @@ describe "TokenizedBuffer", ->
it "updates the tokens with the appropriate invisible characters", ->
buffer = new TextBuffer(text: " \t a line with tabs\tand \tspaces \t ")
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
atom.config.set("editor.showInvisibles", true)
@@ -630,7 +651,9 @@ describe "TokenizedBuffer", ->
it "assigns endOfLineInvisibles to tokenized lines", ->
buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending")
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
atom.config.set('editor.showInvisibles', true)
atom.config.set("editor.invisibles", cr: 'R', eol: 'N')
@@ -651,7 +674,9 @@ describe "TokenizedBuffer", ->
describe "leading and trailing whitespace", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
@@ -709,7 +734,9 @@ describe "TokenizedBuffer", ->
describe ".indentLevel on tokenized lines", ->
beforeEach ->
buffer = atom.project.bufferForPathSync('sample.js')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
describe "when the line is non-empty", ->
@@ -804,7 +831,9 @@ describe "TokenizedBuffer", ->
buffer = atom.project.bufferForPathSync('sample.js')
buffer.insert [10, 0], " // multi-line\n // comment\n // block\n"
buffer.insert [0, 0], "// multi-line\n// comment\n// block\n"
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
fullyTokenize(tokenizedBuffer)
tokenizedBuffer.onDidChange (change) ->
delete change.bufferChange
@@ -881,7 +910,9 @@ describe "TokenizedBuffer", ->
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
buffer.setText('a\nb\nc')
tokenizedBuffer = new TokenizedBuffer({buffer})
tokenizedBuffer = new TokenizedBuffer({
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
})
tokenizeCallback = jasmine.createSpy('onDidTokenize')
tokenizedBuffer.onDidTokenize(tokenizeCallback)
@@ -905,7 +936,7 @@ describe "TokenizedBuffer", ->
registration = atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', -> called = true)
waitsForPromise ->
atom.project.open('sample.js', autoIndent: false).then (o) ->
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
editor = o
waitsForPromise ->

View File

@@ -7,7 +7,7 @@ describe "TokenizedLine", ->
describe "::isOnlyWhitespace()", ->
beforeEach ->
waitsForPromise ->
atom.project.open('coffee.coffee').then (o) -> editor = o
atom.workspace.open('coffee.coffee').then (o) -> editor = o
it "returns true when the line is only whitespace", ->
expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true

View File

@@ -8,7 +8,7 @@ describe "TooltipManager", ->
ctrlY = _.humanizeKeystroke("ctrl-y")
beforeEach ->
manager = new TooltipManager
manager = new TooltipManager(keymapManager: atom.keymaps)
element = document.createElement('div')
element.classList.add('foo')
jasmine.attachToDOM(element)

View File

@@ -4,11 +4,13 @@ fs = require 'fs-plus'
temp = require 'temp'
TextEditor = require '../src/text-editor'
WindowEventHandler = require '../src/window-event-handler'
ipc = require 'ipc'
describe "Window", ->
describe "WindowEventHandler", ->
[projectPath, windowEventHandler] = []
beforeEach ->
atom.uninstallWindowEventHandler()
spyOn(atom, 'hide')
initialPath = atom.project.getPaths()[0]
spyOn(atom, 'getLoadSettings').andCallFake ->
@@ -16,12 +18,12 @@ describe "Window", ->
loadSettings.initialPath = initialPath
loadSettings
atom.project.destroy()
atom.windowEventHandler.unsubscribe()
windowEventHandler = new WindowEventHandler
windowEventHandler = new WindowEventHandler({atomEnvironment: atom, applicationDelegate: atom.applicationDelegate, window, document})
projectPath = atom.project.getPaths()[0]
afterEach ->
windowEventHandler.unsubscribe()
atom.installWindowEventHandler()
describe "when the window is loaded", ->
it "doesn't have .is-blurred on the body tag", ->
@@ -51,64 +53,25 @@ describe "Window", ->
describe "beforeunload event", ->
beforeEach ->
jasmine.unspy(TextEditor.prototype, "shouldPromptToSave")
spyOn(ipc, 'send')
describe "when pane items are modified", ->
it "prompts user to save and calls atom.workspace.confirmClose", ->
editor = null
spyOn(atom.workspace, 'confirmClose').andCallThrough()
spyOn(atom, "confirm").andReturn(2)
editor = null
beforeEach ->
waitsForPromise -> atom.workspace.open("sample.js").then (o) -> editor = o
runs -> editor.insertText("I look different, I feel different.")
waitsForPromise ->
atom.workspace.open("sample.js").then (o) -> editor = o
it "prompts the user to save them, and allows the unload to continue if they confirm", ->
spyOn(atom.workspace, 'confirmClose').andReturn(true)
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.workspace.confirmClose).toHaveBeenCalled()
expect(ipc.send).not.toHaveBeenCalledWith('did-cancel-window-unload')
runs ->
editor.insertText("I look different, I feel different.")
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.workspace.confirmClose).toHaveBeenCalled()
expect(atom.confirm).toHaveBeenCalled()
it "prompts user to save and handler returns true if don't save", ->
editor = null
spyOn(atom, "confirm").andReturn(2)
waitsForPromise ->
atom.workspace.open("sample.js").then (o) -> editor = o
runs ->
editor.insertText("I look different, I feel different.")
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.confirm).toHaveBeenCalled()
it "prompts user to save and handler returns false if dialog is canceled", ->
editor = null
spyOn(atom, "confirm").andReturn(1)
waitsForPromise ->
atom.workspace.open("sample.js").then (o) -> editor = o
runs ->
editor.insertText("I look different, I feel different.")
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.confirm).toHaveBeenCalled()
describe "when the same path is modified in multiple panes", ->
it "prompts to save the item", ->
return
editor = null
filePath = path.join(temp.mkdirSync('atom-file'), 'file.txt')
fs.writeFileSync(filePath, 'hello')
spyOn(atom.workspace, 'confirmClose').andCallThrough()
spyOn(atom, 'confirm').andReturn(0)
waitsForPromise ->
atom.workspace.open(filePath).then (o) -> editor = o
runs ->
atom.workspace.getActivePane().splitRight(copyActiveItem: true)
editor.setText('world')
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.workspace.confirmClose).toHaveBeenCalled()
expect(atom.confirm.callCount).toBe 1
expect(fs.readFileSync(filePath, 'utf8')).toBe 'world'
it "cancels the unload if the user selects cancel", ->
spyOn(atom.workspace, 'confirmClose').andReturn(false)
window.dispatchEvent(new CustomEvent('beforeunload'))
expect(atom.workspace.confirmClose).toHaveBeenCalled()
expect(ipc.send).toHaveBeenCalledWith('did-cancel-window-unload')
describe "when a link is clicked", ->
it "opens the http/https links in an external application", ->
@@ -225,62 +188,6 @@ describe "Window", ->
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
expect(document.activeElement.tabIndex).toBe 7
describe "the window:open-locations event", ->
beforeEach ->
spyOn(atom.workspace, 'open')
atom.project.setPaths([])
describe "when the opened path exists", ->
it "adds it to the project's paths", ->
pathToOpen = __filename
atom.getCurrentWindow().send 'message', 'open-locations', [{pathToOpen}]
waitsFor ->
atom.project.getPaths().length is 1
runs ->
expect(atom.project.getPaths()[0]).toBe __dirname
describe "when the opened path does not exist but its parent directory does", ->
it "adds the parent directory to the project paths", ->
pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt')
atom.getCurrentWindow().send 'message', 'open-locations', [{pathToOpen}]
waitsFor ->
atom.project.getPaths().length is 1
runs ->
expect(atom.project.getPaths()[0]).toBe __dirname
describe "when the opened path is a file", ->
it "opens it in the workspace", ->
pathToOpen = __filename
atom.getCurrentWindow().send 'message', 'open-locations', [{pathToOpen}]
waitsFor ->
atom.workspace.open.callCount is 1
runs ->
expect(atom.workspace.open.mostRecentCall.args[0]).toBe __filename
describe "when the opened path is a directory", ->
it "does not open it in the workspace", ->
pathToOpen = __dirname
atom.getCurrentWindow().send 'message', 'open-locations', [{pathToOpen}]
expect(atom.workspace.open.callCount).toBe 0
describe "when the opened path is a uri", ->
it "adds it to the project's paths as is", ->
pathToOpen = 'remote://server:7644/some/dir/path'
atom.getCurrentWindow().send 'message', 'open-locations', [{pathToOpen}]
waitsFor ->
atom.project.getPaths().length is 1
runs ->
expect(atom.project.getPaths()[0]).toBe pathToOpen
describe "when keydown events occur on the document", ->
it "dispatches the event via the KeymapManager and CommandRegistry", ->
dispatchedCommands = []

View File

@@ -1,6 +1,7 @@
path = require 'path'
temp = require 'temp'
Workspace = require '../src/workspace'
Project = require '../src/project'
Pane = require '../src/pane'
platform = require './spec-helper-platform'
_ = require 'underscore-plus'
@@ -8,11 +9,14 @@ fstream = require 'fstream'
fs = require 'fs-plus'
describe "Workspace", ->
workspace = null
[workspace, setDocumentEdited] = []
beforeEach ->
workspace = atom.workspace
workspace.resetFontSize()
spyOn(atom.applicationDelegate, "confirm")
setDocumentEdited = spyOn(atom.applicationDelegate, 'setWindowDocumentEdited')
atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')])
atom.workspace = workspace = new Workspace
waits(1)
describe "serialization", ->
@@ -21,8 +25,16 @@ describe "Workspace", ->
projectState = atom.project.serialize()
atom.workspace.destroy()
atom.project.destroy()
atom.project = atom.deserializers.deserialize(projectState)
atom.workspace = Workspace.deserialize(workspaceState)
atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)})
atom.project.deserialize(projectState, atom.deserializers)
atom.workspace = new Workspace({
config: atom.config, project: atom.project, packageManager: atom.packages,
grammarRegistry: atom.grammars, deserializerManager: atom.deserializers,
notificationManager: atom.notifications, clipboard: atom.clipboard,
applicationDelegate: atom.applicationDelegate,
viewRegistry: atom.views, assert: atom.assert.bind(atom),
})
atom.workspace.deserialize(workspaceState, atom.deserializers)
describe "when the workspace contains text editors", ->
it "constructs the view with the same panes", ->
@@ -333,7 +345,8 @@ describe "Workspace", ->
describe "when the file is over 20MB", ->
it "prompts the user to make sure they want to open a file this big", ->
spyOn(fs, 'getSizeSync').andReturn 20 * 1048577 # 20MB
spyOn(atom, 'confirm').andCallFake -> selectedButtonIndex
atom.applicationDelegate.confirm.andCallFake -> selectedButtonIndex
atom.applicationDelegate.confirm()
selectedButtonIndex = 1 # cancel
editor = null
@@ -342,16 +355,16 @@ describe "Workspace", ->
runs ->
expect(editor).toBeUndefined()
expect(atom.confirm).toHaveBeenCalled()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
atom.confirm.reset()
atom.applicationDelegate.confirm.reset()
selectedButtonIndex = 0 # open the file
waitsForPromise ->
workspace.open('sample.js').then (e) -> editor = e
runs ->
expect(atom.confirm).toHaveBeenCalled()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
expect(editor.displayBuffer.largeFileMode).toBe true
describe "when passed a path that matches a custom opener", ->
@@ -614,7 +627,13 @@ describe "Workspace", ->
spyOn(jsPackage, 'loadGrammarsSync')
spyOn(coffeePackage, 'loadGrammarsSync')
workspace2 = Workspace.deserialize(state)
workspace2 = new Workspace({
config: atom.config, project: atom.project, packageManager: atom.packages,
notificationManager: atom.notifications, deserializerManager: atom.deserializers,
clipboard: atom.clipboard, viewRegistry: atom.views, grammarRegistry: atom.grammars,
applicationDelegate: atom.applicationDelegate, assert: atom.assert.bind(atom)
})
workspace2.deserialize(state, atom.deserializers)
expect(jsPackage.loadGrammarsSync.callCount).toBe 1
expect(coffeePackage.loadGrammarsSync.callCount).toBe 1
@@ -666,7 +685,13 @@ describe "Workspace", ->
it "updates the title to contain the project's path", ->
document.title = null
workspace2 = Workspace.deserialize(atom.workspace.serialize())
workspace2 = new Workspace({
config: atom.config, project: atom.project, packageManager: atom.packages,
notificationManager: atom.notifications, deserializerManager: atom.deserializers,
clipboard: atom.clipboard, viewRegistry: atom.views, grammarRegistry: atom.grammars,
applicationDelegate: atom.applicationDelegate, assert: atom.assert.bind(atom)
})
workspace2.deserialize(atom.workspace.serialize(), atom.deserializers)
item = atom.workspace.getActivePaneItem()
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
workspace2.destroy()
@@ -679,15 +704,14 @@ describe "Workspace", ->
waitsForPromise -> atom.workspace.open('b')
runs ->
[item1, item2] = atom.workspace.getPaneItems()
spyOn(atom, 'setDocumentEdited')
it "calls atom.setDocumentEdited when the active item changes", ->
it "calls setDocumentEdited when the active item changes", ->
expect(atom.workspace.getActivePaneItem()).toBe item2
item1.insertText('a')
expect(item1.isModified()).toBe true
atom.workspace.getActivePane().activateNextItem()
expect(atom.setDocumentEdited).toHaveBeenCalledWith(true)
expect(setDocumentEdited).toHaveBeenCalledWith(true)
it "calls atom.setDocumentEdited when the active item's modified status changes", ->
expect(atom.workspace.getActivePaneItem()).toBe item2
@@ -695,13 +719,13 @@ describe "Workspace", ->
advanceClock(item2.getBuffer().getStoppedChangingDelay())
expect(item2.isModified()).toBe true
expect(atom.setDocumentEdited).toHaveBeenCalledWith(true)
expect(setDocumentEdited).toHaveBeenCalledWith(true)
item2.undo()
advanceClock(item2.getBuffer().getStoppedChangingDelay())
expect(item2.isModified()).toBe false
expect(atom.setDocumentEdited).toHaveBeenCalledWith(false)
expect(setDocumentEdited).toHaveBeenCalledWith(false)
describe "adding panels", ->
class TestItem
@@ -956,7 +980,7 @@ describe "Workspace", ->
results = []
waitsForPromise ->
atom.project.open('a').then (o) ->
atom.workspace.open('a').then (o) ->
editor = o
editor.setText("Elephant")
@@ -974,7 +998,7 @@ describe "Workspace", ->
results = []
waitsForPromise ->
atom.project.open(temp.openSync().path).then (o) ->
atom.workspace.open(temp.openSync().path).then (o) ->
editor = o
editor.setText("Elephant")
@@ -1070,6 +1094,9 @@ describe "Workspace", ->
search: (directory, regex, options) -> fakeSearch = new FakeSearch(options)
})
waitsFor ->
atom.workspace.directorySearchers.length > 0
it "can override the DefaultDirectorySearcher on a per-directory basis", ->
foreignFilePath = 'ssh://foreign-directory:8080/hello.txt'
numPathsSearchedInDir2 = 1
@@ -1193,7 +1220,7 @@ describe "Workspace", ->
results = []
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
expect(editor.isModified()).toBeFalsy()
@@ -1214,7 +1241,7 @@ describe "Workspace", ->
results = []
waitsForPromise ->
atom.project.open('sample-with-comments.js').then (o) -> editor = o
atom.workspace.open('sample-with-comments.js').then (o) -> editor = o
waitsForPromise ->
atom.workspace.replace /items/gi, 'items', [commentFilePath], (result) ->
@@ -1229,7 +1256,7 @@ describe "Workspace", ->
results = []
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor = o
atom.workspace.open('sample.js').then (o) -> editor = o
runs ->
editor.buffer.setTextInRange([[0, 0], [0, 0]], 'omg')

View File

@@ -0,0 +1,165 @@
_ = require 'underscore-plus'
ipc = require 'ipc'
remote = require 'remote'
shell = require 'shell'
{Disposable} = require 'event-kit'
{getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers'
module.exports =
class ApplicationDelegate
open: (params) ->
ipc.send('open', params)
pickFolder: (callback) ->
responseChannel = "atom-pick-folder-response"
ipc.on responseChannel, (path) ->
ipc.removeAllListeners(responseChannel)
callback(path)
ipc.send("pick-folder", responseChannel)
getCurrentWindow: ->
remote.getCurrentWindow()
closeWindow: ->
ipc.send("call-window-method", "close")
getWindowSize: ->
[width, height] = remote.getCurrentWindow().getSize()
{width, height}
setWindowSize: (width, height) ->
remote.getCurrentWindow().setSize(width, height)
getWindowPosition: ->
[x, y] = remote.getCurrentWindow().getPosition()
{x, y}
setWindowPosition: (x, y) ->
ipc.send("call-window-method", "setPosition", x, y)
centerWindow: ->
ipc.send("call-window-method", "center")
focusWindow: ->
ipc.send("call-window-method", "focus")
showWindow: ->
ipc.send("call-window-method", "show")
hideWindow: ->
ipc.send("call-window-method", "hide")
restartWindow: ->
ipc.send("call-window-method", "restart")
isWindowMaximized: ->
remote.getCurrentWindow().isMaximized()
maximizeWindow: ->
ipc.send("call-window-method", "maximize")
isWindowFullScreen: ->
remote.getCurrentWindow().isFullScreen()
setWindowFullScreen: (fullScreen=false) ->
ipc.send("call-window-method", "setFullScreen", fullScreen)
openWindowDevTools: ->
remote.getCurrentWindow().openDevTools()
toggleWindowDevTools: ->
remote.getCurrentWindow().toggleDevTools()
executeJavaScriptInWindowDevTools: (code) ->
remote.getCurrentWindow().executeJavaScriptInDevTools(code)
setWindowDocumentEdited: (edited) ->
ipc.send("call-window-method", "setDocumentEdited", edited)
setRepresentedFilename: (filename) ->
ipc.send("call-window-method", "setRepresentedFilename", filename)
setRepresentedDirectoryPaths: (paths) ->
loadSettings = getWindowLoadSettings()
loadSettings['initialPaths'] = paths
setWindowLoadSettings(loadSettings)
setAutoHideWindowMenuBar: (autoHide) ->
ipc.send("call-window-method", "setAutoHideMenuBar", autoHide)
setWindowMenuBarVisibility: (visible) ->
remote.getCurrentWindow().setMenuBarVisibility(visible)
getPrimaryDisplayWorkAreaSize: ->
screen = remote.require 'screen'
screen.getPrimaryDisplay().workAreaSize
confirm: ({message, detailedMessage, buttons}) ->
buttons ?= {}
if _.isArray(buttons)
buttonLabels = buttons
else
buttonLabels = Object.keys(buttons)
dialog = remote.require('dialog')
chosen = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info'
message: message
detail: detailedMessage
buttons: buttonLabels
})
if _.isArray(buttons)
chosen
else
callback = buttons[buttonLabels[chosen]]
callback?()
showMessageDialog: (params) ->
showSaveDialog: (params) ->
if _.isString(params)
params = defaultPath: params
else
params = _.clone(params)
params.title ?= 'Save File'
params.defaultPath ?= getWindowLoadSettings().initialPaths[0]
dialog = remote.require('dialog')
dialog.showSaveDialog remote.getCurrentWindow(), params
playBeepSound: ->
shell.beep()
onDidOpenLocations: (callback) ->
outerCallback = (message, detail) ->
if message is 'open-locations'
callback(detail)
ipc.on('message', outerCallback)
new Disposable ->
ipc.removeEventListener('message', outerCallback)
onUpdateAvailable: (callback) ->
outerCallback = (message, detail) ->
if message is 'update-available'
callback(detail)
ipc.on('message', outerCallback)
new Disposable ->
ipc.removeEventListener('message', outerCallback)
onApplicationMenuCommand: (callback) ->
ipc.on('command', callback)
new Disposable ->
ipc.removeEventListener('command', callback)
onContextMenuCommand: (callback) ->
ipc.on('context-command', callback)
new Disposable ->
ipc.removeEventListener('context-command', callback)
didCancelWindowUnload: ->
ipc.send('did-cancel-window-unload')
openExternal: (url) ->
shell.openExternal(url)

View File

@@ -1,9 +1,5 @@
crypto = require 'crypto'
ipc = require 'ipc'
os = require 'os'
path = require 'path'
remote = require 'remote'
shell = require 'shell'
_ = require 'underscore-plus'
{deprecate} = require 'grim'
@@ -14,83 +10,52 @@ Model = require './model'
WindowEventHandler = require './window-event-handler'
StylesElement = require './styles-element'
StorageFolder = require './storage-folder'
{getWindowLoadSettings} = require './window-load-settings-helpers'
registerDefaultCommands = require './register-default-commands'
DeserializerManager = require './deserializer-manager'
ViewRegistry = require './view-registry'
NotificationManager = require './notification-manager'
Config = require './config'
KeymapManager = require './keymap-extensions'
TooltipManager = require './tooltip-manager'
CommandRegistry = require './command-registry'
GrammarRegistry = require './grammar-registry'
StyleManager = require './style-manager'
PackageManager = require './package-manager'
ThemeManager = require './theme-manager'
MenuManager = require './menu-manager'
ContextMenuManager = require './context-menu-manager'
CommandInstaller = require './command-installer'
Clipboard = require './clipboard'
Project = require './project'
Workspace = require './workspace'
PanelContainer = require './panel-container'
Panel = require './panel'
PaneContainer = require './pane-container'
PaneAxis = require './pane-axis'
Pane = require './pane'
Project = require './project'
TextEditor = require './text-editor'
TextBuffer = require 'text-buffer'
Gutter = require './gutter'
WorkspaceElement = require './workspace-element'
PanelContainerElement = require './panel-container-element'
PanelElement = require './panel-element'
PaneContainerElement = require './pane-container-element'
PaneAxisElement = require './pane-axis-element'
PaneElement = require './pane-element'
TextEditorElement = require './text-editor-element'
{createGutterView} = require './gutter-component-helpers'
# Essential: Atom global for dealing with packages, themes, menus, and the window.
#
# An instance of this class is always available as the `atom` global.
module.exports =
class Atom extends Model
class AtomEnvironment extends Model
@version: 1 # Increment this when the serialization format changes
# Load or create the Atom environment in the given mode.
#
# * `mode` A {String} mode that is either 'editor' or 'spec' depending on the
# kind of environment you want to build.
#
# Returns an Atom instance, fully initialized
@loadOrCreate: (mode) ->
startTime = Date.now()
atom = @deserialize(@loadState(mode)) ? new this({mode, @version})
atom.deserializeTimings.atom = Date.now() - startTime
atom
# Deserializes the Atom environment from a state object
@deserialize: (state) ->
new this(state) if state?.version is @version
# Loads and returns the serialized state corresponding to this window
# if it exists; otherwise returns undefined.
@loadState: (mode) ->
if stateKey = @getStateKey(@getLoadSettings().initialPaths, mode)
if state = @getStorageFolder().load(stateKey)
return state
if windowState = @getLoadSettings().windowState
try
JSON.parse(@getLoadSettings().windowState)
catch error
console.warn "Error parsing window state: #{statePath} #{error.stack}", error
# Returns the path where the state for the current window will be
# located if it exists.
@getStateKey: (paths, mode) ->
if mode is 'spec'
'spec'
else if mode is 'editor' and paths?.length > 0
sha1 = crypto.createHash('sha1').update(paths.slice().sort().join("\n")).digest('hex')
"editor-#{sha1}"
else
null
# Get the directory path to Atom's configuration area.
#
# Returns the absolute path to ~/.atom
@getConfigDirPath: ->
@configDirPath ?= process.env.ATOM_HOME
@getStorageFolder: ->
@storageFolder ?= new StorageFolder(@getConfigDirPath())
# Returns the load settings hash associated with the current window.
@getLoadSettings: ->
@loadSettings ?= JSON.parse(decodeURIComponent(location.hash.substr(1)))
cloned = _.deepClone(@loadSettings)
# The loadSettings.windowState could be large, request it only when needed.
cloned.__defineGetter__ 'windowState', =>
@getCurrentWindow().loadSettings.windowState
cloned.__defineSetter__ 'windowState', (value) =>
@getCurrentWindow().loadSettings.windowState = value
cloned
@updateLoadSetting: (key, value) ->
@getLoadSettings()
@loadSettings[key] = value
location.hash = encodeURIComponent(JSON.stringify(@loadSettings))
@getCurrentWindow: ->
remote.getCurrentWindow()
workspaceParentSelectorctor: 'body'
lastUncaughtError: null
###
@@ -150,122 +115,197 @@ class Atom extends Model
###
# Call .loadOrCreate instead
constructor: (@state) ->
@emitter = new Emitter
@disposables = new CompositeDisposable
{@mode} = @state
DeserializerManager = require './deserializer-manager'
@deserializers = new DeserializerManager()
@deserializeTimings = {}
constructor: (params={}) ->
{@applicationDelegate, @window, @document, configDirPath, @enablePersistence} = params
# Sets up the basic services that should be available in all modes
# (both spec and application).
#
# Call after this instance has been assigned to the `atom` global.
initialize: ->
window.onerror = =>
@lastUncaughtError = Array::slice.call(arguments)
[message, url, line, column, originalError] = @lastUncaughtError
{line, column} = mapSourcePosition({source: url, line, column})
eventObject = {message, url, line, column, originalError}
openDevTools = true
eventObject.preventDefault = -> openDevTools = false
@emitter.emit 'will-throw-error', eventObject
if openDevTools
@openDevTools()
@executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
@emitter.emit 'did-throw-error', {message, url, line, column, originalError}
@disposables?.dispose()
@disposables = new CompositeDisposable
@displayWindow() unless @inSpecMode()
@setBodyPlatformClass()
@state = {version: @constructor.version}
@loadTime = null
Config = require './config'
KeymapManager = require './keymap-extensions'
ViewRegistry = require './view-registry'
CommandRegistry = require './command-registry'
TooltipManager = require './tooltip-manager'
NotificationManager = require './notification-manager'
PackageManager = require './package-manager'
Clipboard = require './clipboard'
GrammarRegistry = require './grammar-registry'
ThemeManager = require './theme-manager'
StyleManager = require './style-manager'
ContextMenuManager = require './context-menu-manager'
MenuManager = require './menu-manager'
{devMode, safeMode, resourcePath} = @getLoadSettings()
configDirPath = @getConfigDirPath()
# Add 'exports' to module search path.
exportsPath = path.join(resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
# Still set NODE_PATH since tasks may need it.
process.env.NODE_PATH = exportsPath
@emitter = new Emitter
@disposables = new CompositeDisposable
# Make react.js faster
process.env.NODE_ENV ?= 'production' unless devMode
@deserializers = new DeserializerManager(this)
@deserializeTimings = {}
@views = new ViewRegistry(this)
@config = new Config({configDirPath, resourcePath})
@keymaps = new KeymapManager({configDirPath, resourcePath})
@keymaps.subscribeToFileReadFailure()
@tooltips = new TooltipManager
@notifications = new NotificationManager
@config = new Config({configDirPath, resourcePath, notificationManager: @notifications, @enablePersistence})
@setConfigSchema()
@keymaps = new KeymapManager({configDirPath, resourcePath, notificationManager: @notifications})
@tooltips = new TooltipManager(keymapManager: @keymaps)
@commands = new CommandRegistry
@views = new ViewRegistry
@registerViewProviders()
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
@styles = new StyleManager
document.head.appendChild(new StylesElement)
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode})
@contextMenu = new ContextMenuManager({resourcePath, devMode})
@menu = new MenuManager({resourcePath})
@commands.attach(@window)
@grammars = new GrammarRegistry({@config})
@styles = new StyleManager({configDirPath})
@packages = new PackageManager({
devMode, configDirPath, resourcePath, safeMode, @config, styleManager: @styles,
commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications,
grammarRegistry: @grammars
})
@themes = new ThemeManager({
packageManager: @packages, configDirPath, resourcePath, safeMode, @config,
styleManager: @styles, notificationManager: @notifications, viewRegistry: @views
})
@menu = new MenuManager({resourcePath, keymapManager: @keymaps, packageManager: @packages})
@contextMenu = new ContextMenuManager({resourcePath, devMode, keymapManager: @keymaps})
@packages.setMenuManager(@menu)
@packages.setContextMenuManager(@contextMenu)
@packages.setThemeManager(@themes)
@clipboard = new Clipboard()
@grammars = @deserializers.deserialize(@state.grammars ? @state.syntax) ? new GrammarRegistry()
@disposables.add @packages.onDidActivateInitialPackages => @watchThemes()
Project = require './project'
TextBuffer = require 'text-buffer'
@project = new Project({notificationManager: @notifications, packageManager: @packages, @config})
@commandInstaller = new CommandInstaller(@getVersion(), @applicationDelegate)
@workspace = new Workspace({
@config, @project, packageManager: @packages, grammarRegistry: @grammars, deserializerManager: @deserializers,
notificationManager: @notifications, @applicationDelegate, @clipboard, viewRegistry: @views, assert: @assert.bind(this)
})
@themes.workspace = @workspace
@config.load()
@themes.loadBaseStylesheets()
@initialStyleElements = @styles.getSnapshot()
@themes.initialLoadComplete = true
@setBodyPlatformClass()
@stylesElement = @styles.buildStylesElement()
@document.head.appendChild(@stylesElement)
@keymaps.subscribeToFileReadFailure()
@keymaps.loadBundledKeymaps()
@registerDefaultCommands()
@registerDefaultOpeners()
@registerDefaultDeserializers()
@registerDefaultViewProviders()
@installUncaughtErrorHandler()
@installWindowEventHandler()
@observeAutoHideMenuBar()
setConfigSchema: ->
@config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))}
registerDefaultDeserializers: ->
@deserializers.add(Workspace)
@deserializers.add(PaneContainer)
@deserializers.add(PaneAxis)
@deserializers.add(Pane)
@deserializers.add(Project)
@deserializers.add(TextEditor)
@deserializers.add(TextBuffer)
TokenizedBuffer = require './tokenized-buffer'
DisplayBuffer = require './display-buffer'
TextEditor = require './text-editor'
@windowEventHandler = new WindowEventHandler
registerDefaultCommands: ->
registerDefaultCommands({commandRegistry: @commands, @config, @commandInstaller})
# Register the core views as early as possible in case they are needed for
# package deserialization.
registerViewProviders: ->
Gutter = require './gutter'
Pane = require './pane'
PaneElement = require './pane-element'
PaneContainer = require './pane-container'
PaneContainerElement = require './pane-container-element'
PaneAxis = require './pane-axis'
PaneAxisElement = require './pane-axis-element'
TextEditor = require './text-editor'
TextEditorElement = require './text-editor-element'
{createGutterView} = require './gutter-component-helpers'
registerDefaultViewProviders: ->
@views.addViewProvider Workspace, (model, env) ->
new WorkspaceElement().initialize(model, env)
@views.addViewProvider PanelContainer, (model, env) ->
new PanelContainerElement().initialize(model, env)
@views.addViewProvider Panel, (model, env) ->
new PanelElement().initialize(model, env)
@views.addViewProvider PaneContainer, (model, env) ->
new PaneContainerElement().initialize(model, env)
@views.addViewProvider PaneAxis, (model, env) ->
new PaneAxisElement().initialize(model, env)
@views.addViewProvider Pane, (model, env) ->
new PaneElement().initialize(model, env)
@views.addViewProvider TextEditor, (model, env) ->
new TextEditorElement().initialize(model, env)
@views.addViewProvider(Gutter, createGutterView)
atom.views.addViewProvider PaneContainer, (model) ->
new PaneContainerElement().initialize(model)
atom.views.addViewProvider PaneAxis, (model) ->
new PaneAxisElement().initialize(model)
atom.views.addViewProvider Pane, (model) ->
new PaneElement().initialize(model)
atom.views.addViewProvider TextEditor, (model) ->
new TextEditorElement().initialize(model)
atom.views.addViewProvider(Gutter, createGutterView)
registerDefaultOpeners: ->
@workspace.addOpener (uri) =>
switch uri
when 'atom://.atom/stylesheet'
@workspace.open(@styles.getUserStyleSheetPath())
when 'atom://.atom/keymap'
@workspace.open(@keymaps.getUserKeymapPath())
when 'atom://.atom/config'
@workspace.open(@config.getUserConfigPath())
when 'atom://.atom/init-script'
@workspace.open(@getUserInitScriptPath())
registerDefaultTargetForKeymaps: ->
@keymaps.defaultTarget = @views.getView(@workspace)
observeAutoHideMenuBar: ->
@disposables.add @config.onDidChange 'core.autoHideMenuBar', ({newValue}) =>
@setAutoHideMenuBar(newValue)
@setAutoHideMenuBar(true) if @config.get('core.autoHideMenuBar')
reset: ->
@deserializers.clear()
@registerDefaultDeserializers()
@config.clear()
@setConfigSchema()
@keymaps.clear()
@keymaps.loadBundledKeymaps()
@commands.clear()
@registerDefaultCommands()
@styles.restoreSnapshot(@initialStyleElements)
@menu.clear()
@clipboard.reset()
@notifications.clear()
@contextMenu.clear()
@packages.reset()
@workspace.reset(@packages)
@registerDefaultOpeners()
@project.reset(@packages)
@workspace.subscribeToEvents()
@grammars.clear()
@views.clear()
@registerDefaultViewProviders()
@state.packageStates = {}
delete @state.workspace
destroy: ->
return if not @project
@disposables.dispose()
@workspace?.destroy()
@workspace = null
@themes.workspace = null
@project?.destroy()
@project = null
@commands.clear()
@stylesElement.remove()
@uninstallWindowEventHandler()
###
Section: Event Subscription
@@ -341,12 +381,6 @@ class Atom extends Model
isReleasedVersion: ->
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
# Public: Get the directory path to Atom's configuration area.
#
# Returns the absolute path to `~/.atom`.
getConfigDirPath: ->
@constructor.getConfigDirPath()
# Public: Get the time taken to completely load the current window.
#
# This time include things like loading and activating packages, creating
@@ -361,7 +395,7 @@ class Atom extends Model
#
# Returns an {Object} containing all the load setting key/value pairs.
getLoadSettings: ->
@constructor.getLoadSettings()
getWindowLoadSettings()
###
Section: Managing The Atom Window
@@ -372,7 +406,7 @@ class Atom extends Model
# Calling this method without an options parameter will open a prompt to pick
# a file/folder to open in the new window.
#
# * `options` An {Object} with the following keys:
# * `params` An {Object} with the following keys:
# * `pathsToOpen` An {Array} of {String} paths to open.
# * `newWindow` A {Boolean}, true to always open a new window instead of
# reusing existing windows depending on the paths to open.
@@ -381,8 +415,8 @@ class Atom extends Model
# repository and also loads all the packages in ~/.atom/dev/packages
# * `safeMode` A {Boolean}, true to open the window in safe mode. Safe
# mode prevents all packages installed to ~/.atom/packages from loading.
open: (options) ->
ipc.send('open', options)
open: (params) ->
@applicationDelegate.open(params)
# Extended: Prompt the user to select one or more folders.
#
@@ -390,87 +424,81 @@ class Atom extends Model
# * `paths` An {Array} of {String} paths that the user selected, or `null`
# if the user dismissed the dialog.
pickFolder: (callback) ->
responseChannel = "atom-pick-folder-response"
ipc.on responseChannel, (path) ->
ipc.removeAllListeners(responseChannel)
callback(path)
ipc.send("pick-folder", responseChannel)
@applicationDelegate.pickFolder(callback)
# Essential: Close the current window.
close: ->
@getCurrentWindow().close()
@applicationDelegate.closeWindow()
# Essential: Get the size of current window.
#
# Returns an {Object} in the format `{width: 1000, height: 700}`
getSize: ->
[width, height] = @getCurrentWindow().getSize()
{width, height}
@applicationDelegate.getWindowSize()
# Essential: Set the size of current window.
#
# * `width` The {Number} of pixels.
# * `height` The {Number} of pixels.
setSize: (width, height) ->
@getCurrentWindow().setSize(width, height)
@applicationDelegate.setWindowSize(width, height)
# Essential: Get the position of current window.
#
# Returns an {Object} in the format `{x: 10, y: 20}`
getPosition: ->
[x, y] = @getCurrentWindow().getPosition()
{x, y}
@applicationDelegate.getWindowPosition()
# Essential: Set the position of current window.
#
# * `x` The {Number} of pixels.
# * `y` The {Number} of pixels.
setPosition: (x, y) ->
ipc.send('call-window-method', 'setPosition', x, y)
@applicationDelegate.setWindowPosition(x, y)
# Extended: Get the current window
getCurrentWindow: ->
@constructor.getCurrentWindow()
@applicationDelegate.getCurrentWindow()
# Extended: Move current window to the center of the screen.
center: ->
ipc.send('call-window-method', 'center')
@applicationDelegate.centerWindow()
# Extended: Focus the current window.
focus: ->
ipc.send('call-window-method', 'focus')
window.focus()
@applicationDelegate.focusWindow()
@window.focus()
# Extended: Show the current window.
show: ->
ipc.send('call-window-method', 'show')
@applicationDelegate.showWindow()
# Extended: Hide the current window.
hide: ->
ipc.send('call-window-method', 'hide')
@applicationDelegate.hideWindow()
# Extended: Reload the current window.
reload: ->
ipc.send('call-window-method', 'restart')
@applicationDelegate.restartWindow()
# Extended: Returns a {Boolean} that is `true` if the current window is maximized.
isMaximized: ->
@getCurrentWindow().isMaximized()
@applicationDelegate.isWindowMaximized()
maximize: ->
ipc.send('call-window-method', 'maximize')
@applicationDelegate.maximizeWindow()
# Extended: Returns a {Boolean} that is `true` if the current window is in full screen mode.
isFullScreen: ->
@getCurrentWindow().isFullScreen()
@applicationDelegate.isWindowFullScreen()
# Extended: Set the full screen state of the current window.
setFullScreen: (fullScreen=false) ->
ipc.send('call-window-method', 'setFullScreen', fullScreen)
@applicationDelegate.setWindowFullScreen(fullScreen)
if fullScreen
document.body.classList.add("fullscreen")
@document.body.classList.add("fullscreen")
else
document.body.classList.remove("fullscreen")
@document.body.classList.remove("fullscreen")
# Extended: Toggle the full screen state of the current window.
toggleFullScreen: ->
@@ -546,8 +574,7 @@ class Atom extends Model
if @isValidDimensions(dimensions)
dimensions
else
screen = remote.require 'screen'
{width, height} = screen.getPrimaryDisplay().workAreaSize
{width, height} = @applicationDelegate.getPrimaryDisplayWorkAreaSize()
{x: 0, y: 0, width: Math.min(1024, width), height}
restoreWindowDimensions: ->
@@ -565,37 +592,34 @@ class Atom extends Model
return if @inSpecMode()
workspaceElement = @views.getView(@workspace)
backgroundColor = window.getComputedStyle(workspaceElement)['background-color']
window.localStorage.setItem('atom:window-background-color', backgroundColor)
backgroundColor = @window.getComputedStyle(workspaceElement)['background-color']
@window.localStorage.setItem('atom:window-background-color', backgroundColor)
# Call this method when establishing a real application window.
startEditorWindow: ->
{safeMode} = @getLoadSettings()
CommandInstaller = require './command-installer'
commandInstaller = new CommandInstaller(@getVersion())
commandInstaller.installAtomCommand false, (error) ->
@commandInstaller.installAtomCommand false, (error) ->
console.warn error.message if error?
commandInstaller.installApmCommand false, (error) ->
@commandInstaller.installApmCommand false, (error) ->
console.warn error.message if error?
@loadConfig()
@keymaps.loadBundledKeymaps()
@themes.loadBaseStylesheets()
@disposables.add(@applicationDelegate.onDidOpenLocations(@openLocations.bind(this)))
@disposables.add(@applicationDelegate.onApplicationMenuCommand(@dispatchApplicationMenuCommand.bind(this)))
@disposables.add(@applicationDelegate.onContextMenuCommand(@dispatchContextMenuCommand.bind(this)))
@listenForUpdates()
@registerDefaultTargetForKeymaps()
@packages.loadPackages()
@deserializeEditorWindow()
@document.body.appendChild(@views.getView(@workspace))
@watchProjectPath()
@packages.activate()
@keymaps.loadUserKeymap()
@requireUserInitScript() unless safeMode
@requireUserInitScript() unless @getLoadSettings().safeMode
@menu.update()
@disposables.add @config.onDidChange 'core.autoHideMenuBar', ({newValue}) =>
@setAutoHideMenuBar(newValue)
@setAutoHideMenuBar(true) if @config.get('core.autoHideMenuBar')
@openInitialEmptyEditorIfNecessary()
@@ -603,36 +627,56 @@ class Atom extends Model
return if not @project
@storeWindowBackground()
@state.grammars = @grammars.serialize()
@state.grammars = {grammarOverridesByPath: @grammars.grammarOverridesByPath}
@state.project = @project.serialize()
@state.workspace = @workspace.serialize()
@packages.deactivatePackages()
@state.packageStates = @packages.packageStates
@saveSync()
@windowState = null
removeEditorWindow: ->
return if not @project
@workspace?.destroy()
@workspace = null
@project?.destroy()
@project = null
@windowEventHandler?.unsubscribe()
@state.fullScreen = @isFullScreen()
@saveStateSync()
openInitialEmptyEditorIfNecessary: ->
return unless @config.get('core.openEmptyEditorOnStart')
if @getLoadSettings().initialPaths?.length is 0 and @workspace.getPaneItems().length is 0
@workspace.open(null)
installUncaughtErrorHandler: ->
@previousWindowErrorHandler = @window.onerror
@window.onerror = =>
@lastUncaughtError = Array::slice.call(arguments)
[message, url, line, column, originalError] = @lastUncaughtError
{line, column} = mapSourcePosition({source: url, line, column})
eventObject = {message, url, line, column, originalError}
openDevTools = true
eventObject.preventDefault = -> openDevTools = false
@emitter.emit 'will-throw-error', eventObject
if openDevTools
@openDevTools()
@executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
@emitter.emit 'did-throw-error', {message, url, line, column, originalError}
uninstallUncaughtErrorHandler: ->
@window.onerror = @previousWindowErrorHandler
installWindowEventHandler: ->
@windowEventHandler = new WindowEventHandler({atomEnvironment: this, @applicationDelegate, @window, @document})
uninstallWindowEventHandler: ->
@windowEventHandler?.unsubscribe()
###
Section: Messaging the User
###
# Essential: Visually and audibly trigger a beep.
beep: ->
shell.beep() if @config.get('core.audioBeep')
@applicationDelegate.playBeepSound() if @config.get('core.audioBeep')
@emitter.emit 'did-beep'
# Essential: A flexible way to open a dialog akin to an alert dialog.
@@ -655,25 +699,8 @@ class Atom extends Model
# button names and the values are callbacks to invoke when clicked.
#
# Returns the chosen button index {Number} if the buttons option was an array.
confirm: ({message, detailedMessage, buttons}={}) ->
buttons ?= {}
if _.isArray(buttons)
buttonLabels = buttons
else
buttonLabels = Object.keys(buttons)
dialog = remote.require('dialog')
chosen = dialog.showMessageBox @getCurrentWindow(),
type: 'info'
message: message
detail: detailedMessage
buttons: buttonLabels
if _.isArray(buttons)
chosen
else
callback = buttons[buttonLabels[chosen]]
callback?()
confirm: (params={}) ->
@applicationDelegate.confirm(params)
###
Section: Managing the Dev Tools
@@ -681,15 +708,15 @@ class Atom extends Model
# Extended: Open the dev tools for the current window.
openDevTools: ->
ipc.send('call-window-method', 'openDevTools')
@applicationDelegate.openWindowDevTools()
# Extended: Toggle the visibility of the dev tools for the current window.
toggleDevTools: ->
ipc.send('call-window-method', 'toggleDevTools')
@applicationDelegate.toggleWindowDevTools()
# Extended: Execute code in dev tools.
executeJavaScriptInDevTools: (code) ->
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
@applicationDelegate.executeJavaScriptInWindowDevTools(code)
###
Section: Private
@@ -706,62 +733,19 @@ class Atom extends Model
false
deserializeProject: ->
Project = require './project'
startTime = Date.now()
@project ?= @deserializers.deserialize(@state.project) ? new Project()
@deserializeTimings.project = Date.now() - startTime
deserializeWorkspace: ->
Workspace = require './workspace'
startTime = Date.now()
@workspace = Workspace.deserialize(@state.workspace) ? new Workspace
@deserializeTimings.workspace = Date.now() - startTime
workspaceElement = @views.getView(@workspace)
@keymaps.defaultTarget = workspaceElement
document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement)
deserializePackageStates: ->
@packages.packageStates = @state.packageStates ? {}
delete @state.packageStates
deserializeEditorWindow: ->
@deserializePackageStates()
@deserializeProject()
@deserializeWorkspace()
loadConfig: ->
@config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))}
@config.load()
loadThemes: ->
@themes.load()
watchThemes: ->
@themes.onDidChangeActiveThemes =>
# Only reload stylesheets from non-theme packages
for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
pack.reloadStylesheets?()
return
# Notify the browser project of the window's current project path
watchProjectPath: ->
@disposables.add @project.onDidChangePaths =>
@constructor.updateLoadSetting('initialPaths', @project.getPaths())
exit: (status) ->
app = remote.require('app')
app.emit('will-exit')
remote.process.exit(status)
@applicationDelegate.setRepresentedDirectoryPaths(@project.getPaths())
setDocumentEdited: (edited) ->
ipc.send('call-window-method', 'setDocumentEdited', edited)
@applicationDelegate.setWindowDocumentEdited?(edited)
setRepresentedFilename: (filename) ->
ipc.send('call-window-method', 'setRepresentedFilename', filename)
@applicationDelegate.setWindowRepresentedFilename?(filename)
addProjectFolder: ->
@pickFolder (selectedPaths = []) =>
@@ -771,27 +755,61 @@ class Atom extends Model
callback(showSaveDialogSync())
showSaveDialogSync: (options={}) ->
if _.isString(options)
options = defaultPath: options
else
options = _.clone(options)
currentWindow = @getCurrentWindow()
dialog = remote.require('dialog')
options.title ?= 'Save File'
options.defaultPath ?= @project?.getPaths()[0]
dialog.showSaveDialog currentWindow, options
@applicationDelegate.showSaveDialog(options)
saveSync: ->
if storageKey = @constructor.getStateKey(@project?.getPaths(), @mode)
@constructor.getStorageFolder().store(storageKey, @state)
saveStateSync: ->
return unless @enablePersistence
if storageKey = @getStateKey(@project?.getPaths())
@getStorageFolder().store(storageKey, @state)
else
@getCurrentWindow().loadSettings.windowState = JSON.stringify(@state)
crashMainProcess: ->
remote.process.crash()
loadStateSync: ->
return unless @enablePersistence
crashRenderProcess: ->
process.crash()
startTime = Date.now()
if stateKey = @getStateKey(@getLoadSettings().initialPaths)
if state = @getStorageFolder().load(stateKey)
@state = state
if not @state? and windowState = @getLoadSettings().windowState
try
if state = JSON.parse(@getLoadSettings().windowState)
@state = state
catch error
console.warn "Error parsing window state: #{statePath} #{error.stack}", error
@deserializeTimings.atom = Date.now() - startTime
if grammarOverridesByPath = @state.grammars?.grammarOverridesByPath
@grammars.grammarOverridesByPath = grammarOverridesByPath
@setFullScreen(@state.fullScreen)
@packages.packageStates = @state.packageStates ? {}
startTime = Date.now()
@project.deserialize(@state.project, @deserializers) if @state.project?
@deserializeTimings.project = Date.now() - startTime
startTime = Date.now()
@workspace.deserialize(@state.workspace, @deserializers) if @state.workspace?
@deserializeTimings.workspace = Date.now() - startTime
getStateKey: (paths) ->
if paths?.length > 0
sha1 = crypto.createHash('sha1').update(paths.slice().sort().join("\n")).digest('hex')
"editor-#{sha1}"
else
null
getConfigDirPath: ->
@configDirPath ?= process.env.ATOM_HOME
getStorageFolder: ->
@storageFolder ?= new StorageFolder(@getConfigDirPath())
getUserInitScriptPath: ->
initScriptPath = fs.resolve(@getConfigDirPath(), 'init', ['js', 'coffee'])
@@ -802,44 +820,52 @@ class Atom extends Model
try
require(userInitScriptPath) if fs.isFileSync(userInitScriptPath)
catch error
atom.notifications.addError "Failed to load `#{userInitScriptPath}`",
@notifications.addError "Failed to load `#{userInitScriptPath}`",
detail: error.message
dismissable: true
# Require the module with the given globals.
#
# The globals will be set on the `window` object and removed after the
# require completes.
#
# * `id` The {String} module name or path.
# * `globals` An optional {Object} to set as globals during require.
requireWithGlobals: (id, globals={}) ->
existingGlobals = {}
for key, value of globals
existingGlobals[key] = window[key]
window[key] = value
require(id)
for key, value of existingGlobals
if value is undefined
delete window[key]
else
window[key] = value
return
onUpdateAvailable: (callback) ->
@emitter.on 'update-available', callback
updateAvailable: (details) ->
@emitter.emit 'update-available', details
listenForUpdates: ->
@disposables.add(@applicationDelegate.onUpdateAvailable(@updateAvailable.bind(this)))
setBodyPlatformClass: ->
document.body.classList.add("platform-#{process.platform}")
@document.body.classList.add("platform-#{process.platform}")
setAutoHideMenuBar: (autoHide) ->
ipc.send('call-window-method', 'setAutoHideMenuBar', autoHide)
ipc.send('call-window-method', 'setMenuBarVisibility', not autoHide)
@applicationDelegate.setAutoHideWindowMenuBar(autoHide)
@applicationDelegate.setWindowMenuBarVisibility(not autoHide)
dispatchApplicationMenuCommand: (command, arg) ->
activeElement = @document.activeElement
# Use the workspace element if body has focus
if activeElement is @document.body and workspaceElement = @views.getView(@workspace)
activeElement = workspaceElement
@commands.dispatch(activeElement, command, arg)
dispatchContextMenuCommand: (command, args...) ->
@commands.dispatch(@contextMenu.activeElement, command, args)
openLocations: (locations) ->
needsProjectPaths = @project?.getPaths().length is 0
for {pathToOpen, initialLine, initialColumn} in locations
if pathToOpen? and needsProjectPaths
if fs.existsSync(pathToOpen)
@project.addPath(pathToOpen)
else if fs.existsSync(path.dirname(pathToOpen))
@project.addPath(path.dirname(pathToOpen))
else
@project.addPath(pathToOpen)
unless fs.isDirectorySync(pathToOpen)
@workspace?.open(pathToOpen, {initialLine, initialColumn})
return
# Preserve this deprecation until 2.0. Sorry. Should have removed Q sooner.
Promise.prototype.done = (callback) ->

View File

@@ -16,6 +16,8 @@ net = require 'net'
url = require 'url'
{EventEmitter} = require 'events'
_ = require 'underscore-plus'
FindParentDir = null
Resolve = null
LocationSuffixRegExp = /(:\d+)(:\d+)?$/
@@ -63,7 +65,7 @@ class AtomApplication
exit: (status) -> app.exit(status)
constructor: (options) ->
{@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath} = options
{@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, timeout} = options
@socketPath = null if options.test
@@ -87,9 +89,9 @@ class AtomApplication
else
@loadState() or @openPath(options)
openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile, profileStartup}) ->
openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout}) ->
if test
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory, logFile})
@runTests({headless: true, @resourcePath, executedFrom, pathsToOpen, logFile, timeout})
else if pathsToOpen.length > 0
@openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup})
else if urlsToOpen.length > 0
@@ -163,7 +165,6 @@ class AtomApplication
devMode: @focusedWindow()?.devMode
safeMode: @focusedWindow()?.safeMode
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: @devResourcePath, safeMode: @focusedWindow()?.safeMode)
@on 'application:quit', -> app.quit()
@on 'application:new-window', -> @openPath(_.extend(windowDimensions: @focusedWindow()?.getDimensions(), getLoadSettings()))
@on 'application:new-file', -> (@focusedWindow() ? this).openPath()
@@ -218,11 +219,6 @@ class AtomApplication
@killAllProcesses()
@deleteSocketFile()
app.on 'will-exit', =>
@saveState(false)
@killAllProcesses()
@deleteSocketFile()
app.on 'open-file', (event, pathToOpen) =>
event.preventDefault()
@openPath({pathToOpen})
@@ -253,8 +249,8 @@ class AtomApplication
win = BrowserWindow.fromWebContents(event.sender)
@applicationMenu.update(win, template, keystrokesByCommand)
ipc.on 'run-package-specs', (event, specDirectory) =>
@runSpecs({resourcePath: @devResourcePath, specDirectory: specDirectory, exitWhenDone: false})
ipc.on 'run-package-specs', (event, packageSpecPath) =>
@runTests({resourcePath: @devResourcePath, pathsToOpen: [packageSpecPath], headless: false})
ipc.on 'command', (event, command) =>
@emit(command)
@@ -271,13 +267,19 @@ class AtomApplication
@promptForPath "folder", (selectedPaths) ->
event.sender.send(responseChannel, selectedPaths)
ipc.on 'cancel-window-close', =>
ipc.on 'did-cancel-window-unload', =>
@quitting = false
clipboard = require '../safe-clipboard'
ipc.on 'write-text-to-selection-clipboard', (event, selectedText) ->
clipboard.writeText(selectedText, 'selection')
ipc.on 'write-to-stdout', (event, output) ->
process.stdout.write(output)
ipc.on 'write-to-stderr', (event, output) ->
process.stderr.write(output)
# Public: Executes the given command.
#
# If it isn't handled globally, delegate to the currently focused window.
@@ -395,12 +397,12 @@ class AtomApplication
else
if devMode
try
bootstrapScript = require.resolve(path.join(@devResourcePath, 'src', 'window-bootstrap'))
windowInitializationScript = require.resolve(path.join(@devResourcePath, 'src', 'initialize-application-window'))
resourcePath = @devResourcePath
bootstrapScript ?= require.resolve('../window-bootstrap')
windowInitializationScript ?= require.resolve('../initialize-application-window')
resourcePath ?= @resourcePath
openedWindow = new AtomWindow({locationsToOpen, bootstrapScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup})
openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup})
if pidToKillWhenClosed?
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
@@ -475,9 +477,9 @@ class AtomApplication
if pack?
if pack.urlMain
packagePath = @packages.resolvePackagePath(packageName)
bootstrapScript = path.resolve(packagePath, pack.urlMain)
windowInitializationScript = path.resolve(packagePath, pack.urlMain)
windowDimensions = @focusedWindow()?.getDimensions()
new AtomWindow({bootstrapScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions})
new AtomWindow({windowInitializationScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions})
else
console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}"
else
@@ -486,25 +488,64 @@ class AtomApplication
# Opens up a new {AtomWindow} to run specs within.
#
# options -
# :exitWhenDone - A Boolean that, if true, will close the window upon
# :headless - A Boolean that, if true, will close the window upon
# completion.
# :resourcePath - The path to include specs from.
# :specPath - The directory to load specs from.
# :safeMode - A Boolean that, if true, won't run specs from ~/.atom/packages
# and ~/.atom/dev/packages, defaults to false.
runSpecs: ({exitWhenDone, resourcePath, specDirectory, logFile, safeMode}) ->
runTests: ({headless, resourcePath, executedFrom, pathsToOpen, logFile, safeMode, timeout}) ->
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
resourcePath = @resourcePath
try
bootstrapScript = require.resolve(path.resolve(@devResourcePath, 'spec', 'spec-bootstrap'))
catch error
bootstrapScript = require.resolve(path.resolve(__dirname, '..', '..', 'spec', 'spec-bootstrap'))
timeoutInSeconds = Number.parseFloat(timeout)
unless Number.isNaN(timeoutInSeconds)
timeoutHandler = ->
console.log "The test suite has timed out because it has been running for more than #{timeoutInSeconds} seconds."
process.exit(124) # Use the same exit code as the UNIX timeout util.
setTimeout(timeoutHandler, timeoutInSeconds * 1000)
try
windowInitializationScript = require.resolve(path.resolve(@devResourcePath, 'src', 'initialize-test-window'))
catch error
windowInitializationScript = require.resolve(path.resolve(__dirname, '..', '..', 'src', 'initialize-test-window'))
testPaths = []
if pathsToOpen?
for pathToOpen in pathsToOpen
testPaths.push(path.resolve(executedFrom, fs.normalize(pathToOpen)))
if testPaths.length is 0
process.stderr.write 'Error: Specify at least one test path\n\n'
process.exit(1)
legacyTestRunnerPath = @resolveLegacyTestRunnerPath()
testRunnerPath = @resolveTestRunnerPath(testPaths[0])
isSpec = true
devMode = true
safeMode ?= false
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile, safeMode})
new AtomWindow({windowInitializationScript, resourcePath, headless, isSpec, devMode, testRunnerPath, legacyTestRunnerPath, testPaths, logFile, safeMode})
resolveTestRunnerPath: (testPath) ->
FindParentDir ?= require 'find-parent-dir'
if packageRoot = FindParentDir.sync(testPath, 'package.json')
packageMetadata = require(path.join(packageRoot, 'package.json'))
if packageMetadata.atomTestRunner
Resolve ?= require('resolve')
if testRunnerPath = Resolve.sync(packageMetadata.atomTestRunner, basedir: packageRoot, extensions: Object.keys(require.extensions))
return testRunnerPath
else
process.stderr.write "Error: Could not resolve test runner path '#{packageMetadata.atomTestRunner}'"
process.exit(1)
@resolveLegacyTestRunnerPath()
resolveLegacyTestRunnerPath: ->
try
require.resolve(path.resolve(@devResourcePath, 'spec', 'jasmine-test-runner'))
catch error
require.resolve(path.resolve(__dirname, '..', '..', 'spec', 'jasmine-test-runner'))
locationForPathToOpen: (pathToOpen, executedFrom='') ->
return {pathToOpen} unless pathToOpen

View File

@@ -19,7 +19,7 @@ class AtomWindow
isSpec: null
constructor: (settings={}) ->
{@resourcePath, pathToOpen, locationsToOpen, @isSpec, @exitWhenDone, @safeMode, @devMode} = settings
{@resourcePath, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings
locationsToOpen ?= [{pathToOpen}] if pathToOpen
locationsToOpen ?= []
@@ -29,6 +29,10 @@ class AtomWindow
'web-preferences':
'direct-write': true
'subpixel-font-scaling': true
if @isSpec
options['web-preferences']['page-visibility'] = true
# Don't set icon on Windows so the exe's ico will be used as window and
# taskbar's icon. See https://github.com/atom/atom/issues/4811 for more.
if process.platform is 'linux'
@@ -131,7 +135,7 @@ class AtomWindow
@browserWindow.destroy() if chosen is 0
@browserWindow.webContents.on 'crashed', =>
global.atomApplication.exit(100) if @exitWhenDone
global.atomApplication.exit(100) if @headless
chosen = dialog.showMessageBox @browserWindow,
type: 'warning'

View File

@@ -99,9 +99,9 @@ parseCommandLine = ->
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
options.boolean('profile-startup').describe('profile-startup', 'Create a profile of the startup execution time.')
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the Atom source directory and enable dev-mode.')
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the directory from which to run package specs (default: Atom\'s spec directory).')
options.boolean('safe').describe('safe', 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.')
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).')
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.')
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
options.string('socket-path')
@@ -121,7 +121,7 @@ parseCommandLine = ->
safeMode = args['safe']
pathsToOpen = args._
test = args['test']
specDirectory = args['spec-directory']
timeout = args['timeout']
newWindow = args['new-window']
pidToKillWhenClosed = args['pid'] if args['wait']
logFile = args['log-file']
@@ -133,18 +133,7 @@ parseCommandLine = ->
if args['resource-path']
devMode = true
resourcePath = args['resource-path']
else
# Set resourcePath based on the specDirectory if running specs on atom core
if specDirectory?
packageDirectoryPath = path.join(specDirectory, '..')
packageManifestPath = path.join(packageDirectoryPath, 'package.json')
if fs.statSyncNoException(packageManifestPath)
try
packageManifest = JSON.parse(fs.readFileSync(packageManifestPath))
resourcePath = packageDirectoryPath if packageManifest.name is 'atom'
if devMode
resourcePath ?= devResourcePath
resourcePath ?= devResourcePath if devMode
unless fs.statSyncNoException(resourcePath)
resourcePath = path.dirname(path.dirname(__dirname))
@@ -157,7 +146,7 @@ parseCommandLine = ->
devResourcePath = normalizeDriveLetterName(devResourcePath)
{resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory,
logFile, socketPath, profileStartup}
version, pidToKillWhenClosed, devMode, safeMode, newWindow,
logFile, socketPath, profileStartup, timeout}
start()

View File

@@ -14,8 +14,12 @@ clipboard = require './safe-clipboard'
# ```
module.exports =
class Clipboard
metadata: null
signatureForMetadata: null
constructor: ->
@reset()
reset: ->
@metadata = null
@signatureForMetadata = null
# Creates an `md5` hash of some text.
#

View File

@@ -26,7 +26,7 @@ symlinkCommandWithPrivilegeSync = (sourcePath, destinationPath) ->
module.exports =
class CommandInstaller
constructor: (@appVersion) ->
constructor: (@appVersion, @applicationDelegate) ->
getInstallDirectory: ->
"/usr/local/bin"
@@ -36,7 +36,7 @@ class CommandInstaller
installShellCommandsInteractively: ->
showErrorDialog = (error) ->
atom.confirm
@applicationDelegate.confirm
message: "Failed to install shell commands"
detailedMessage: error.message
@@ -48,7 +48,7 @@ class CommandInstaller
if error?
showErrorDialog(error)
else
atom.confirm
@applicationDelegate.confirm
message: "Commands installed."
detailedMessage: "The shell commands `atom` and `apm` are installed."

View File

@@ -44,15 +44,23 @@ SequenceCount = 0
# ```
module.exports =
class CommandRegistry
constructor: (@rootNode) ->
constructor: ->
@rootNode = null
@clear()
clear: ->
@registeredCommands = {}
@selectorBasedListenersByCommandName = {}
@inlineListenersByCommandName = {}
@emitter = new Emitter
attach: (@rootNode) ->
@commandRegistered(command) for command of @selectorBasedListenersByCommandName
@commandRegistered(command) for command of @inlineListenersByCommandName
destroy: ->
for commandName of @registeredCommands
window.removeEventListener(commandName, @handleCommandEvent, true)
@rootNode.removeEventListener(commandName, @handleCommandEvent, true)
return
# Public: Add one or more command listeners associated with a selector.
@@ -253,8 +261,8 @@ class CommandRegistry
matched
commandRegistered: (commandName) ->
unless @registeredCommands[commandName]
window.addEventListener(commandName, @handleCommandEvent, true)
if @rootNode? and not @registeredCommands[commandName]
@rootNode.addEventListener(commandName, @handleCommandEvent, true)
@registeredCommands[commandName] = true
class SelectorBasedListener

View File

@@ -334,7 +334,13 @@ class Config
value
# Created during initialization, available as `atom.config`
constructor: ({@configDirPath, @resourcePath}={}) ->
constructor: ({@configDirPath, @resourcePath, @notificationManager, @enablePersistence}={}) ->
if @enablePersistence?
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@clear()
clear: ->
@emitter = new Emitter
@schema =
type: 'object'
@@ -343,11 +349,8 @@ class Config
@settings = {}
@scopedSettingsStore = new ScopedPropertyStore
@configFileHasErrors = false
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@transactDepth = 0
@savePending = false
@requestLoad = _.debounce(@loadUserConfig, 100)
@requestSave = =>
@savePending = true
@@ -357,6 +360,8 @@ class Config
@save()
debouncedSave = _.debounce(save, 100)
shouldNotAccessFileSystem: -> not @enablePersistence
###
Section: Config Subscription
###
@@ -727,7 +732,7 @@ class Config
###
initializeConfigDirectory: (done) ->
return if fs.existsSync(@configDirPath)
return if fs.existsSync(@configDirPath) or @shouldNotAccessFileSystem()
fs.makeTreeSync(@configDirPath)
@@ -743,6 +748,8 @@ class Config
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
loadUserConfig: ->
return if @shouldNotAccessFileSystem()
unless fs.existsSync(@configFilePath)
fs.makeTreeSync(path.dirname(@configFilePath))
CSON.writeFileSync(@configFilePath, {})
@@ -766,6 +773,8 @@ class Config
@notifyFailure(message, detail)
observeUserConfig: ->
return if @shouldNotAccessFileSystem()
try
@watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) =>
@requestLoad() if eventType is 'change' and @watchSubscription?
@@ -782,9 +791,11 @@ class Config
@watchSubscription = null
notifyFailure: (errorMessage, detail) ->
atom.notifications.addError(errorMessage, {detail, dismissable: true})
@notificationManager.addError(errorMessage, {detail, dismissable: true})
save: ->
return if @shouldNotAccessFileSystem()
allSettings = {'*': @settings}
allSettings = _.extend allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath())
try

View File

@@ -4,6 +4,7 @@ CSON = require 'season'
fs = require 'fs-plus'
{calculateSpecificity, validateSelector} = require 'clear-cut'
{Disposable} = require 'event-kit'
remote = require 'remote'
MenuHelpers = require './menu-helpers'
platformContextMenu = require('../package.json')?._atomMenu?['context-menu']
@@ -40,11 +41,11 @@ platformContextMenu = require('../package.json')?._atomMenu?['context-menu']
# {::add} for more information.
module.exports =
class ContextMenuManager
constructor: ({@resourcePath, @devMode}) ->
constructor: ({@resourcePath, @devMode, @keymapManager}) ->
@definitions = {'.overlayer': []} # TODO: Remove once color picker package stops touching private data
@clear()
atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems()
@keymapManager.onDidLoadBundledKeymaps => @loadPlatformItems()
loadPlatformItems: ->
if platformContextMenu?
@@ -175,7 +176,7 @@ class ContextMenuManager
menuTemplate = @templateForEvent(event)
return unless menuTemplate?.length > 0
atom.getCurrentWindow().emit('context-menu', menuTemplate)
remote.getCurrentWindow().emit('context-menu', menuTemplate)
return
clear: ->

View File

@@ -16,7 +16,7 @@ class Cursor extends Model
visible: true
# Instantiated by a {TextEditor}
constructor: ({@editor, @marker, id}) ->
constructor: ({@editor, @marker, @config, id}) ->
@emitter = new Emitter
@assignId(id)
@@ -158,7 +158,7 @@ class Cursor extends Model
[before, after] = @editor.getTextInBufferRange(range)
return false if /\s/.test(before) or /\s/.test(after)
nonWordCharacters = atom.config.get('editor.nonWordCharacters', scope: @getScopeDescriptor()).split('')
nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor()).split('')
_.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after)
# Public: Returns whether this cursor is between a word's start and end.
@@ -605,7 +605,7 @@ class Cursor extends Model
# Returns a {RegExp}.
wordRegExp: ({includeNonWordCharacters}={}) ->
includeNonWordCharacters ?= true
nonWordCharacters = atom.config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
segments = ["^[\t ]*$"]
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
if includeNonWordCharacters
@@ -620,7 +620,7 @@ class Cursor extends Model
#
# Returns a {RegExp}.
subwordRegExp: (options={}) ->
nonWordCharacters = atom.config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor())
lowercaseLetters = 'a-z\\u00DF-\\u00F6\\u00F8-\\u00FF'
uppercaseLetters = 'A-Z\\u00C0-\\u00D6\\u00D8-\\u00DE'
snakeCamelSegment = "[#{uppercaseLetters}]?[#{lowercaseLetters}]+"

View File

@@ -6,12 +6,12 @@
module.exports =
class CustomGutterComponent
constructor: ({@gutter}) ->
constructor: ({@gutter, @views}) ->
@decorationNodesById = {}
@decorationItemsById = {}
@visible = true
@domNode = atom.views.getView(@gutter)
@domNode = @views.getView(@gutter)
@decorationsNode = @domNode.firstChild
# Clear the contents in case the domNode is being reused.
@decorationsNode.innerHTML = ''

View File

@@ -21,7 +21,7 @@
# ```
module.exports =
class DeserializerManager
constructor: ->
constructor: (@atomEnvironment) ->
@deserializers = {}
# Public: Register the given class(es) as deserializers.
@@ -29,7 +29,10 @@ class DeserializerManager
# * `deserializers` One or more deserializers to register. A deserializer can
# be any object with a `.name` property and a `.deserialize()` method. A
# common approach is to register a *constructor* as the deserializer for its
# instances by adding a `.deserialize()` class method.
# instances by adding a `.deserialize()` class method. When your method is
# called, it will be passed serialized state as the first argument and the
# {Atom} environment object as the second argument, which is useful if you
# wish to avoid referencing the `atom` global.
add: (deserializers...) ->
@deserializers[deserializer.name] = deserializer for deserializer in deserializers
new Disposable =>
@@ -39,15 +42,13 @@ class DeserializerManager
# Public: Deserialize the state and params.
#
# * `state` The state {Object} to deserialize.
# * `params` The params {Object} to pass as the second arguments to the
# deserialize method of the deserializer.
deserialize: (state, params) ->
deserialize: (state) ->
return unless state?
if deserializer = @get(state)
stateVersion = state.get?('version') ? state.version
return if deserializer.version? and deserializer.version isnt stateVersion
deserializer.deserialize(state, params)
deserializer.deserialize(state, @atomEnvironment)
else
console.warn "No deserializer found for", state
@@ -59,3 +60,6 @@ class DeserializerManager
name = state.get?('deserializer') ? state.deserializer
@deserializers[name]
clear: ->
@deserializers = {}

View File

@@ -26,17 +26,29 @@ class DisplayBuffer extends Model
height: null
width: null
@deserialize: (state) ->
state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer)
@deserialize: (state, atomEnvironment) ->
state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer, atomEnvironment)
state.config = atomEnvironment.config
state.assert = atomEnvironment.assert
state.grammarRegistry = atomEnvironment.grammars
state.packageManager = atomEnvironment.packages
new this(state)
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles, @largeFileMode}={}) ->
constructor: (params={}) ->
super
{
tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles,
@largeFileMode, @config, @assert, @grammarRegistry, @packageManager
} = params
@emitter = new Emitter
@disposables = new CompositeDisposable
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, ignoreInvisibles, @largeFileMode})
@tokenizedBuffer ?= new TokenizedBuffer({
tabLength, buffer, ignoreInvisibles, @largeFileMode, @config,
@grammarRegistry, @packageManager, @assert
})
@buffer = @tokenizedBuffer.buffer
@charWidthsByScope = {}
@markers = {}
@@ -61,29 +73,29 @@ class DisplayBuffer extends Model
oldConfigSettings = @configSettings
@configSettings =
scrollPastEnd: atom.config.get('editor.scrollPastEnd', scope: scopeDescriptor)
softWrap: atom.config.get('editor.softWrap', scope: scopeDescriptor)
softWrapAtPreferredLineLength: atom.config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
softWrapHangingIndent: atom.config.get('editor.softWrapHangingIndent', scope: scopeDescriptor)
preferredLineLength: atom.config.get('editor.preferredLineLength', scope: scopeDescriptor)
scrollPastEnd: @config.get('editor.scrollPastEnd', scope: scopeDescriptor)
softWrap: @config.get('editor.softWrap', scope: scopeDescriptor)
softWrapAtPreferredLineLength: @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
softWrapHangingIndent: @config.get('editor.softWrapHangingIndent', scope: scopeDescriptor)
preferredLineLength: @config.get('editor.preferredLineLength', scope: scopeDescriptor)
subscriptions.add atom.config.onDidChange 'editor.softWrap', scope: scopeDescriptor, ({newValue}) =>
subscriptions.add @config.onDidChange 'editor.softWrap', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrap = newValue
@updateWrappedScreenLines()
subscriptions.add atom.config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, ({newValue}) =>
subscriptions.add @config.onDidChange 'editor.softWrapHangingIndent', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrapHangingIndent = newValue
@updateWrappedScreenLines()
subscriptions.add atom.config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, ({newValue}) =>
subscriptions.add @config.onDidChange 'editor.softWrapAtPreferredLineLength', scope: scopeDescriptor, ({newValue}) =>
@configSettings.softWrapAtPreferredLineLength = newValue
@updateWrappedScreenLines() if @isSoftWrapped()
subscriptions.add atom.config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, ({newValue}) =>
subscriptions.add @config.onDidChange 'editor.preferredLineLength', scope: scopeDescriptor, ({newValue}) =>
@configSettings.preferredLineLength = newValue
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
@updateWrappedScreenLines() if @isSoftWrapped() and @config.get('editor.softWrapAtPreferredLineLength', scope: scopeDescriptor)
subscriptions.add atom.config.observe 'editor.scrollPastEnd', scope: scopeDescriptor, (value) =>
subscriptions.add @config.observe 'editor.scrollPastEnd', scope: scopeDescriptor, (value) =>
@configSettings.scrollPastEnd = value
@updateWrappedScreenLines() if oldConfigSettings? and not _.isEqual(oldConfigSettings, @configSettings)
@@ -97,7 +109,10 @@ class DisplayBuffer extends Model
largeFileMode: @largeFileMode
copy: ->
newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength(), @largeFileMode})
newDisplayBuffer = new DisplayBuffer({
@buffer, tabLength: @getTabLength(), @largeFileMode, @config, @assert,
@grammarRegistry, @packageManager
})
for marker in @findMarkers(displayBufferId: @id)
marker.copy(displayBufferId: newDisplayBuffer.id)
@@ -1080,8 +1095,8 @@ class DisplayBuffer extends Model
tokenizedLinesCount = @tokenizedBuffer.getLineCount()
bufferLinesCount = @buffer.getLineCount()
atom.assert screenLinesCount is tokenizedLinesCount, "Display buffer line count out of sync with tokenized buffer", (error) ->
@assert screenLinesCount is tokenizedLinesCount, "Display buffer line count out of sync with tokenized buffer", (error) ->
error.metadata = {screenLinesCount, tokenizedLinesCount, bufferLinesCount}
atom.assert screenLinesCount is bufferLinesCount, "Display buffer line count out of sync with buffer", (error) ->
@assert screenLinesCount is bufferLinesCount, "Display buffer line count out of sync with buffer", (error) ->
error.metadata = {screenLinesCount, tokenizedLinesCount, bufferLinesCount}

View File

@@ -48,7 +48,7 @@ isValidGitDirectorySync = (directory) ->
module.exports =
class GitRepositoryProvider
constructor: (@project) ->
constructor: (@project, @config) ->
# Keys are real paths that end in `.git`.
# Values are the corresponding GitRepository objects.
@pathToRepository = {}
@@ -75,7 +75,7 @@ class GitRepositoryProvider
gitDirPath = gitDir.getPath()
repo = @pathToRepository[gitDirPath]
unless repo
repo = GitRepository.open(gitDirPath, project: @project)
repo = GitRepository.open(gitDirPath, {@project, @config})
return null unless repo
repo.onDidDestroy(=> delete @pathToRepository[gitDirPath])
@pathToRepository[gitDirPath] = repo

View File

@@ -80,7 +80,7 @@ class GitRepository
for submodulePath, submoduleRepo of @repo.submodules
submoduleRepo.upstream = {ahead: 0, behind: 0}
{@project, refreshOnWindowFocus} = options
{@project, @config, refreshOnWindowFocus} = options
refreshOnWindowFocus ?= true
if refreshOnWindowFocus
@@ -443,25 +443,10 @@ class GitRepository
# Subscribes to editor view event.
checkoutHeadForEditor: (editor) ->
filePath = editor.getPath()
return unless filePath
fileName = basename(filePath)
checkoutHead = =>
if filePath = editor.getPath()
editor.buffer.reload() if editor.buffer.isModified()
@checkoutHead(filePath)
if atom.config.get('editor.confirmCheckoutHeadRevision')
atom.confirm
message: 'Confirm Checkout HEAD Revision'
detailedMessage: "Are you sure you want to discard all changes to \"#{fileName}\" since the last Git commit?"
buttons:
OK: checkoutHead
Cancel: null
else
checkoutHead()
# Returns the corresponding {Repository}
getRepo: (path) ->
if @repo?

View File

@@ -14,19 +14,9 @@ PathSplitRegex = new RegExp("[/.]")
# language-specific comment regexes. See {::getProperty} for more details.
module.exports =
class GrammarRegistry extends FirstMate.GrammarRegistry
@deserialize: ({grammarOverridesByPath}) ->
grammarRegistry = new GrammarRegistry()
grammarRegistry.grammarOverridesByPath = grammarOverridesByPath
grammarRegistry
atom.deserializers.add(this)
constructor: ->
constructor: ({@config}={}) ->
super(maxTokensPerLine: 100)
serialize: ->
{deserializer: @constructor.name, @grammarOverridesByPath}
createToken: (value, scopes) -> new Token({value, scopes})
# Extended: Select a grammar for the given file path and file contents.
@@ -70,7 +60,7 @@ class GrammarRegistry extends FirstMate.GrammarRegistry
pathScore = -1
fileTypes = grammar.fileTypes
if customFileTypes = atom.config.get('core.customFileTypes')?[grammar.scopeName]
if customFileTypes = @config.get('core.customFileTypes')?[grammar.scopeName]
fileTypes = fileTypes.concat(customFileTypes)
for fileType, i in fileTypes
@@ -133,6 +123,3 @@ class GrammarRegistry extends FirstMate.GrammarRegistry
clearGrammarOverrides: ->
@grammarOverridesByPath = {}
undefined
clearObservers: ->
@emitter = new Emitter

View File

@@ -7,7 +7,7 @@ LineNumberGutterComponent = require './line-number-gutter-component'
module.exports =
class GutterContainerComponent
constructor: ({@onLineNumberGutterMouseDown, @editor, @domElementPool}) ->
constructor: ({@onLineNumberGutterMouseDown, @editor, @domElementPool, @views}) ->
# An array of objects of the form: {name: {String}, component: {Object}}
@gutterComponents = []
@gutterComponentsByGutterName = {}
@@ -39,10 +39,10 @@ class GutterContainerComponent
gutterComponent = @gutterComponentsByGutterName[gutter.name]
if not gutterComponent
if gutter.name is 'line-number'
gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @editor, gutter, @domElementPool})
gutterComponent = new LineNumberGutterComponent({onMouseDown: @onLineNumberGutterMouseDown, @editor, gutter, @domElementPool, @views})
@lineNumberGutterComponent = gutterComponent
else
gutterComponent = new CustomGutterComponent({gutter})
gutterComponent = new CustomGutterComponent({gutter, @views})
if visible then gutterComponent.showNode() else gutterComponent.hideNode()
# Pass the gutter only the state that it needs.

View File

@@ -0,0 +1,34 @@
# Like sands through the hourglass, so are the days of our lives.
path = require 'path'
require './window'
{getWindowLoadSettings} = require './window-load-settings-helpers'
{resourcePath, isSpec, devMode} = getWindowLoadSettings()
# Add application-specific exports to module search path.
exportsPath = path.join(resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath
# Make React faster
process.env.NODE_ENV ?= 'production' unless devMode
AtomEnvironment = require './atom-environment'
ApplicationDelegate = require './application-delegate'
window.atom = new AtomEnvironment({
window, document,
applicationDelegate: new ApplicationDelegate,
configDirPath: process.env.ATOM_HOME
enablePersistence: true
})
atom.displayWindow()
atom.loadStateSync()
atom.startEditorWindow()
# Workaround for focus getting cleared upon window creation
windowFocused = ->
window.removeEventListener('focus', windowFocused)
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
window.addEventListener('focus', windowFocused)

View File

@@ -0,0 +1,59 @@
# Start the crash reporter before anything else.
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
remote = require 'remote'
exitWithStatusCode = (status) ->
remote.require('app').emit('will-quit')
remote.process.exit(status)
try
path = require 'path'
ipc = require 'ipc'
{getWindowLoadSettings} = require './window-load-settings-helpers'
AtomEnvironment = require '../src/atom-environment'
ApplicationDelegate = require '../src/application-delegate'
# Show window synchronously so a focusout doesn't fire on input elements
# that are focused in the very first spec run.
remote.getCurrentWindow().show() unless getWindowLoadSettings().headless
handleKeydown = (event) ->
# Reload: cmd-r / ctrl-r
if (event.metaKey or event.ctrlKey) and event.keyCode is 82
ipc.send('call-window-method', 'restart')
# Toggle Dev Tools: cmd-alt-i / ctrl-alt-i
if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73
ipc.send('call-window-method', 'toggleDevTools')
# Reload: cmd-w / ctrl-w
if (event.metaKey or event.ctrlKey) and event.keyCode is 87
ipc.send('call-window-method', 'close')
window.addEventListener('keydown', handleKeydown, true)
# Add 'exports' to module search path.
exportsPath = path.join(getWindowLoadSettings().resourcePath, 'exports')
require('module').globalPaths.push(exportsPath)
process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it.
document.title = "Spec Suite"
legacyTestRunner = require(getWindowLoadSettings().legacyTestRunnerPath)
testRunner = require(getWindowLoadSettings().testRunnerPath)
promise = testRunner({
logFile: getWindowLoadSettings().logFile
headless: getWindowLoadSettings().headless
testPaths: getWindowLoadSettings().testPaths
buildAtomEnvironment: (params) -> new AtomEnvironment(params)
buildDefaultApplicationDelegate: (params) -> new ApplicationDelegate()
legacyTestRunner: legacyTestRunner
})
promise.then(exitWithStatusCode) if getWindowLoadSettings().headless
catch error
if getWindowLoadSettings().headless
console.error(error.stack ? error)
exitWithStatusCode(1)
else
throw error

View File

@@ -20,6 +20,8 @@ KeymapManager::loadBundledKeymaps = ->
@emitter.emit 'did-load-bundled-keymaps'
KeymapManager::getUserKeymapPath = ->
return "" unless @configDirPath?
if userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
userKeymapPath
else
@@ -41,11 +43,11 @@ KeymapManager::loadUserKeymap = ->
[this document][watches] for more info.
[watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path
"""
atom.notifications.addError(message, {dismissable: true})
@notificationManager.addError(message, {dismissable: true})
else
detail = error.path
stack = error.stack
atom.notifications.addFatalError(error.message, {detail, stack, dismissable: true})
@notificationManager.addFatalError(error.message, {detail, stack, dismissable: true})
KeymapManager::subscribeToFileReadFailure = ->
@onDidFailToReadFile (error) =>
@@ -57,6 +59,6 @@ KeymapManager::subscribeToFileReadFailure = ->
else
error.message
atom.notifications.addError(message, {detail, dismissable: true})
@notificationManager.addError(message, {detail, dismissable: true})
module.exports = KeymapManager

View File

@@ -8,7 +8,7 @@ class LanguageMode
# Sets up a `LanguageMode` for the given {TextEditor}.
#
# editor - The {TextEditor} to associate with
constructor: (@editor) ->
constructor: (@editor, @config) ->
{@buffer} = @editor
destroy: ->
@@ -327,7 +327,7 @@ class LanguageMode
@editor.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
getRegexForProperty: (scopeDescriptor, property) ->
if pattern = atom.config.get(property, scope: scopeDescriptor)
if pattern = @config.get(property, scope: scopeDescriptor)
new OnigRegExp(pattern)
increaseIndentRegexForScopeDescriptor: (scopeDescriptor) ->
@@ -343,8 +343,8 @@ class LanguageMode
@getRegexForProperty(scopeDescriptor, 'editor.foldEndPattern')
commentStartAndEndStringsForScope: (scope) ->
commentStartEntry = atom.config.getAll('editor.commentStart', {scope})[0]
commentEndEntry = _.find atom.config.getAll('editor.commentEnd', {scope}), (entry) ->
commentStartEntry = @config.getAll('editor.commentStart', {scope})[0]
commentEndEntry = _.find @config.getAll('editor.commentEnd', {scope}), (entry) ->
entry.scopeSelector is commentStartEntry.scopeSelector
commentStartString = commentStartEntry?.value
commentEndString = commentEndEntry?.value

View File

@@ -7,12 +7,12 @@ module.exports =
class LineNumberGutterComponent extends TiledComponent
dummyLineNumberNode: null
constructor: ({@onMouseDown, @editor, @gutter, @domElementPool}) ->
constructor: ({@onMouseDown, @editor, @gutter, @domElementPool, @views}) ->
@visible = true
@dummyLineNumberComponent = LineNumbersTileComponent.createDummy(@domElementPool)
@domNode = atom.views.getView(@gutter)
@domNode = @views.getView(@gutter)
@lineNumbersNode = @domNode.firstChild
@lineNumbersNode.innerHTML = ''

View File

@@ -21,7 +21,7 @@ module.exports =
class LinesComponent extends TiledComponent
placeholderTextDiv: null
constructor: ({@presenter, @useShadowDOM, @domElementPool}) ->
constructor: ({@presenter, @useShadowDOM, @domElementPool, @assert, @grammars}) ->
@domNode = document.createElement('div')
@domNode.classList.add('lines')
@tilesNode = document.createElement("div")
@@ -72,7 +72,7 @@ class LinesComponent extends TiledComponent
@oldState.indentGuidesVisible = @newState.indentGuidesVisible
buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool})
buildComponentForTile: (id) -> new LinesTileComponent({id, @presenter, @domElementPool, @assert, @grammars})
buildEmptyState: ->
{tiles: {}}

View File

@@ -13,8 +13,8 @@ cloneObject = (object) ->
module.exports =
class LinesTileComponent
constructor: ({@presenter, @id, @domElementPool}) ->
@tokenIterator = new TokenIterator
constructor: ({@presenter, @id, @domElementPool, @assert, grammars}) ->
@tokenIterator = new TokenIterator(grammarRegistry: grammars)
@measuredLines = new Set
@lineNodesByLineId = {}
@screenRowsByLineId = {}

View File

@@ -3,8 +3,8 @@ TokenIterator = require './token-iterator'
module.exports =
class LinesYardstick
constructor: (@model, @presenter, @lineNodesProvider) ->
@tokenIterator = new TokenIterator
constructor: (@model, @presenter, @lineNodesProvider, grammarRegistry) ->
@tokenIterator = new TokenIterator({grammarRegistry})
@rangeForMeasurement = document.createRange()
@invalidateCache()

View File

@@ -59,12 +59,12 @@ platformMenu = require('../package.json')?._atomMenu?.menu
# See {::add} for more info about adding menu's directly.
module.exports =
class MenuManager
constructor: ({@resourcePath}) ->
constructor: ({@resourcePath, @keymapManager, @packageManager}) ->
@pendingUpdateOperation = null
@template = []
atom.keymaps.onDidLoadBundledKeymaps => @loadPlatformItems()
atom.keymaps.onDidReloadKeymap => @update()
atom.packages.onDidActivateInitialPackages => @sortPackagesMenu()
@keymapManager.onDidLoadBundledKeymaps => @loadPlatformItems()
@keymapManager.onDidReloadKeymap => @update()
@packageManager.onDidActivateInitialPackages => @sortPackagesMenu()
# Public: Adds the given items to the application menu.
#
@@ -96,6 +96,10 @@ class MenuManager
@unmerge(@template, item) for item in items
@update()
clear: ->
@template = []
@update()
# Should the binding for the given selector be included in the menu
# commands.
#
@@ -143,7 +147,7 @@ class MenuManager
includedBindings = []
unsetKeystrokes = new Set
for binding in atom.keymaps.getKeyBindings() when @includeSelector(binding.selector)
for binding in @keymapManager.getKeyBindings() when @includeSelector(binding.selector)
includedBindings.push(binding)
if binding.command is 'unset!'
unsetKeystrokes.add(binding.keystrokes)
@@ -191,7 +195,10 @@ class MenuManager
# Get an {Array} of {String} classes for the given element.
classesForElement: (element) ->
element?.classList.toString().split(' ') ? []
if classList = element?.classList
Array::slice.apply(classList)
else
[]
sortPackagesMenu: ->
packagesMenu = _.find @template, ({label}) -> MenuHelpers.normalizeLabel(label) is 'Packages'

View File

@@ -1,6 +1,6 @@
module.exports =
class OverlayManager
constructor: (@presenter, @container) ->
constructor: (@presenter, @container, @views) ->
@overlaysById = {}
render: (state) ->
@@ -28,7 +28,7 @@ class OverlayManager
@presenter.setOverlayDimensions(decorationId, itemView.offsetWidth, itemView.offsetHeight, contentMargin)
renderOverlay: (state, decorationId, {item, pixelPosition, class: klass}) ->
itemView = atom.views.getView(item)
itemView = @views.getView(item)
cachedOverlay = @overlaysById[decorationId]
unless overlayNode = cachedOverlay?.overlayNode
overlayNode = document.createElement('atom-overlay')

View File

@@ -1,8 +1,10 @@
path = require 'path'
normalizePackageData = null
_ = require 'underscore-plus'
{Emitter} = require 'event-kit'
fs = require 'fs-plus'
CSON = require 'season'
ServiceHub = require 'service-hub'
Package = require './package'
@@ -26,15 +28,21 @@ ThemePackage = require './theme-package'
# settings and also by calling `enablePackage()/disablePackage()`.
module.exports =
class PackageManager
constructor: ({configDirPath, @devMode, safeMode, @resourcePath}) ->
constructor: (params) ->
{
configDirPath, @devMode, safeMode, @resourcePath, @config, @styleManager,
@notificationManager, @keymapManager, @commandRegistry, @grammarRegistry
} = params
@emitter = new Emitter
@activationHookEmitter = new Emitter
@packageDirPaths = []
unless safeMode
if configDirPath? and not safeMode
if @devMode
@packageDirPaths.push(path.join(configDirPath, "dev", "packages"))
@packageDirPaths.push(path.join(configDirPath, "packages"))
@packagesCache = require('../package.json')?._atomPackages ? {}
@loadedPackages = {}
@activePackages = {}
@packageStates = {}
@@ -43,6 +51,17 @@ class PackageManager
@packageActivators = []
@registerPackageActivator(this, ['atom', 'textmate'])
setContextMenuManager: (@contextMenuManager) ->
setMenuManager: (@menuManager) ->
setThemeManager: (@themeManager) ->
reset: ->
@serviceHub.clear()
@deactivatePackages()
@packageStates = {}
###
Section: Event Subscription
###
@@ -185,7 +204,7 @@ class PackageManager
#
# Returns a {Boolean}.
isPackageDisabled: (name) ->
_.include(atom.config.get('core.disabledPackages') ? [], name)
_.include(@config.get('core.disabledPackages') ? [], name)
###
Section: Accessing active packages
@@ -269,7 +288,7 @@ class PackageManager
packages = []
for packagePath in @getAvailablePackagePaths()
name = path.basename(packagePath)
metadata = @getLoadedPackage(name)?.metadata ? Package.loadMetadata(packagePath, true)
metadata = @getLoadedPackage(name)?.metadata ? @loadPackageMetadata(packagePath, true)
packages.push(metadata)
packages
@@ -292,7 +311,7 @@ class PackageManager
@packageDependencies
hasAtomEngine: (packagePath) ->
metadata = Package.loadMetadata(packagePath, true)
metadata = @loadPackageMetadata(packagePath, true)
metadata?.engines?.atom?
unobserveDisabledPackages: ->
@@ -300,7 +319,7 @@ class PackageManager
@disabledPackagesSubscription = null
observeDisabledPackages: ->
@disabledPackagesSubscription ?= atom.config.onDidChange 'core.disabledPackages', ({newValue, oldValue}) =>
@disabledPackagesSubscription ?= @config.onDidChange 'core.disabledPackages', ({newValue, oldValue}) =>
packagesToEnable = _.difference(oldValue, newValue)
packagesToDisable = _.difference(newValue, oldValue)
@@ -313,7 +332,7 @@ class PackageManager
@packagesWithKeymapsDisabledSubscription = null
observePackagesWithKeymapsDisabled: ->
@packagesWithKeymapsDisabledSubscription ?= atom.config.onDidChange 'core.packagesWithKeymapsDisabled', ({newValue, oldValue}) =>
@packagesWithKeymapsDisabledSubscription ?= @config.onDidChange 'core.packagesWithKeymapsDisabled', ({newValue, oldValue}) =>
keymapsToEnable = _.difference(oldValue, newValue)
keymapsToDisable = _.difference(newValue, oldValue)
@@ -340,7 +359,7 @@ class PackageManager
return pack if pack = @getLoadedPackage(name)
try
metadata = Package.loadMetadata(packagePath) ? {}
metadata = @loadPackageMetadata(packagePath) ? {}
catch error
@handleMetadataError(error, packagePath)
return null
@@ -350,10 +369,15 @@ class PackageManager
console.warn "Could not load #{metadata.name}@#{metadata.version} because it uses deprecated APIs that have been removed."
return null
options = {
path: packagePath, metadata, packageManager: this, @config, @styleManager,
@commandRegistry, @keymapManager, @devMode, @notificationManager,
@grammarRegistry, @themeManager, @menuManager, @contextMenuManager
}
if metadata.theme
pack = new ThemePackage(packagePath, metadata)
pack = new ThemePackage(options)
else
pack = new Package(packagePath, metadata)
pack = new Package(options)
pack.load()
@loadedPackages[pack.name] = pack
@emitter.emit 'did-load-package', pack
@@ -392,7 +416,7 @@ class PackageManager
activatePackages: (packages) ->
promises = []
atom.config.transact =>
@config.transact =>
for pack in packages
promise = @activatePackage(pack.name)
promises.push(promise) unless pack.hasActivationCommands()
@@ -423,7 +447,7 @@ class PackageManager
# Deactivate all packages
deactivatePackages: ->
atom.config.transact =>
@config.transact =>
@deactivatePackage(pack.name) for pack in @getLoadedPackages()
return
@unobserveDisabledPackages()
@@ -443,7 +467,7 @@ class PackageManager
detail = "#{error.message} in #{metadataPath}"
stack = "#{error.stack}\n at #{metadataPath}:1:1"
message = "Failed to load the #{path.basename(packagePath)} package"
atom.notifications.addError(message, {stack, detail, dismissable: true})
@notificationManager.addError(message, {stack, detail, dismissable: true})
uninstallDirectory: (directory) ->
symlinkPromise = new Promise (resolve) ->
@@ -456,3 +480,40 @@ class PackageManager
[isSymLink, isDir] = values
if not isSymLink and isDir
fs.remove directory, ->
reloadActivePackageStyleSheets: ->
for pack in @getActivePackages() when pack.getType() isnt 'theme'
pack.reloadStylesheets?()
return
isBundledPackagePath: (packagePath) ->
if @devMode
return false unless @resourcePath.startsWith("#{process.resourcesPath}#{path.sep}")
@resourcePathWithTrailingSlash ?= "#{@resourcePath}#{path.sep}"
packagePath?.startsWith(@resourcePathWithTrailingSlash)
loadPackageMetadata: (packagePath, ignoreErrors=false) ->
packageName = path.basename(packagePath)
if @isBundledPackagePath(packagePath)
metadata = @packagesCache[packageName]?.metadata
unless metadata?
if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
try
metadata = CSON.readFileSync(metadataPath)
@normalizePackageMetadata(metadata)
catch error
throw error unless ignoreErrors
metadata ?= {}
unless typeof metadata.name is 'string' and metadata.name.length > 0
metadata.name = packageName
metadata
normalizePackageMetadata: (metadata) ->
unless metadata?._id
normalizePackageData ?= require 'normalize-package-data'
normalizePackageData(metadata)
if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string'
metadata.repository.url = metadata.repository.url.replace(/^git\+/, '')

View File

@@ -1,5 +1,4 @@
path = require 'path'
normalizePackageData = null
_ = require 'underscore-plus'
async = require 'async'
@@ -11,44 +10,10 @@ ModuleCache = require './module-cache'
ScopedProperties = require './scoped-properties'
BufferedProcess = require './buffered-process'
packagesCache = require('../package.json')?._atomPackages ? {}
# Extended: Loads and activates a package's main module and resources such as
# stylesheets, keymaps, grammar, editor properties, and menus.
module.exports =
class Package
@isBundledPackagePath: (packagePath) ->
if atom.packages.devMode
return false unless atom.packages.resourcePath.startsWith("#{process.resourcesPath}#{path.sep}")
@resourcePathWithTrailingSlash ?= "#{atom.packages.resourcePath}#{path.sep}"
packagePath?.startsWith(@resourcePathWithTrailingSlash)
@normalizeMetadata: (metadata) ->
unless metadata?._id
normalizePackageData ?= require 'normalize-package-data'
normalizePackageData(metadata)
if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string'
metadata.repository.url = metadata.repository.url.replace(/^git\+/, '')
@loadMetadata: (packagePath, ignoreErrors=false) ->
packageName = path.basename(packagePath)
if @isBundledPackagePath(packagePath)
metadata = packagesCache[packageName]?.metadata
unless metadata?
if metadataPath = CSON.resolve(path.join(packagePath, 'package'))
try
metadata = CSON.readFileSync(metadataPath)
@normalizeMetadata(metadata)
catch error
throw error unless ignoreErrors
metadata ?= {}
unless typeof metadata.name is 'string' and metadata.name.length > 0
metadata.name = packageName
metadata
keymaps: null
menus: null
stylesheets: null
@@ -64,10 +29,16 @@ class Package
Section: Construction
###
constructor: (@path, @metadata) ->
constructor: (params) ->
{
@path, @metadata, @packageManager, @config, @styleManager, @commandRegistry,
@keymapManager, @devMode, @notificationManager, @grammarRegistry, @themeManager,
@menuManager, @contextMenuManager
} = params
@emitter = new Emitter
@metadata ?= Package.loadMetadata(@path)
@bundledPackage = Package.isBundledPackagePath(@path)
@metadata ?= @packageManager.loadPackageMetadata(@path)
@bundledPackage = @packageManager.isBundledPackagePath(@path)
@name = @metadata?.name ? path.basename(@path)
ModuleCache.add(@path, @metadata)
@reset()
@@ -89,10 +60,10 @@ class Package
###
enable: ->
atom.config.removeAtKeyPath('core.disabledPackages', @name)
@config.removeAtKeyPath('core.disabledPackages', @name)
disable: ->
atom.config.pushAtKeyPath('core.disabledPackages', @name)
@config.pushAtKeyPath('core.disabledPackages', @name)
isTheme: ->
@metadata?.theme?
@@ -132,7 +103,6 @@ class Package
@activationPromise ?=
new Promise (resolve, reject) =>
@resolveActivationPromise = resolve
@rejectActivationPromise = reject
@measure 'activateTime', =>
try
@activateResources()
@@ -150,7 +120,7 @@ class Package
@activateConfig()
@activateStylesheets()
if @mainModule? and not @mainActivated
@mainModule.activate?(atom.packages.getPackageState(@name) ? {})
@mainModule.activate?(@packageManager.getPackageState(@name) ? {})
@mainActivated = true
@activateServices()
catch error
@@ -164,7 +134,7 @@ class Package
@requireMainModule() unless @mainModule?
if @mainModule?
if @mainModule.config? and typeof @mainModule.config is 'object'
atom.config.setSchema @name, {type: 'object', properties: @mainModule.config}
@config.setSchema @name, {type: 'object', properties: @mainModule.config}
@mainModule.activateConfig?()
@configActivated = true
@@ -182,13 +152,13 @@ class Package
else
context = undefined
@stylesheetDisposables.add(atom.styles.addStyleSheet(source, {sourcePath, priority, context}))
@stylesheetDisposables.add(@styleManager.addStyleSheet(source, {sourcePath, priority, context}))
@stylesheetsActivated = true
activateResources: ->
@activationDisposables = new CompositeDisposable
keymapIsDisabled = _.include(atom.config.get("core.packagesWithKeymapsDisabled") ? [], @name)
keymapIsDisabled = _.include(@config.get("core.packagesWithKeymapsDisabled") ? [], @name)
if keymapIsDisabled
@deactivateKeymaps()
else
@@ -197,14 +167,14 @@ class Package
for [menuPath, map] in @menus when map['context-menu']?
try
itemsBySelector = map['context-menu']
@activationDisposables.add(atom.contextMenu.add(itemsBySelector))
@activationDisposables.add(@contextMenuManager.add(itemsBySelector))
catch error
if error.code is 'EBADSELECTOR'
error.message += " in #{menuPath}"
error.stack += "\n at #{menuPath}:1:1"
throw error
@activationDisposables.add(atom.menu.add(map['menu'])) for [menuPath, map] in @menus when map['menu']?
@activationDisposables.add(@menuManager.add(map['menu'])) for [menuPath, map] in @menus when map['menu']?
unless @grammarsActivated
grammar.activate() for grammar in @grammars
@@ -218,8 +188,8 @@ class Package
@keymapDisposables = new CompositeDisposable()
@keymapDisposables.add(atom.keymaps.add(keymapPath, map)) for [keymapPath, map] in @keymaps
atom.menu.update()
@keymapDisposables.add(@keymapManager.add(keymapPath, map)) for [keymapPath, map] in @keymaps
@menuManager.update()
@keymapActivated = true
@@ -227,7 +197,7 @@ class Package
return if not @keymapActivated
@keymapDisposables?.dispose()
atom.menu.update()
@menuManager.update()
@keymapActivated = false
@@ -243,24 +213,24 @@ class Package
for version, methodName of versions
if typeof @mainModule[methodName] is 'function'
servicesByVersion[version] = @mainModule[methodName]()
@activationDisposables.add atom.packages.serviceHub.provide(name, servicesByVersion)
@activationDisposables.add @packageManager.serviceHub.provide(name, servicesByVersion)
for name, {versions} of @metadata.consumedServices
for version, methodName of versions
if typeof @mainModule[methodName] is 'function'
@activationDisposables.add atom.packages.serviceHub.consume(name, version, @mainModule[methodName].bind(@mainModule))
@activationDisposables.add @packageManager.serviceHub.consume(name, version, @mainModule[methodName].bind(@mainModule))
return
loadKeymaps: ->
if @bundledPackage and packagesCache[@name]?
@keymaps = (["#{atom.packages.resourcePath}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of packagesCache[@name].keymaps)
if @bundledPackage and @packageManager.packagesCache[@name]?
@keymaps = (["#{@packageManager.resourcePath}#{path.sep}#{keymapPath}", keymapObject] for keymapPath, keymapObject of @packageManager.packagesCache[@name].keymaps)
else
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath) ? {}]
return
loadMenus: ->
if @bundledPackage and packagesCache[@name]?
@menus = (["#{atom.packages.resourcePath}#{path.sep}#{menuPath}", menuObject] for menuPath, menuObject of packagesCache[@name].menus)
if @bundledPackage and @packageManager.packagesCache[@name]?
@menus = (["#{@packageManager.resourcePath}#{path.sep}#{menuPath}", menuObject] for menuPath, menuObject of @packageManager.packagesCache[@name].menus)
else
@menus = @getMenuPaths().map (menuPath) -> [menuPath, CSON.readFileSync(menuPath) ? {}]
return
@@ -280,8 +250,8 @@ class Package
fs.listSync(menusDirPath, ['cson', 'json'])
loadStylesheets: ->
@stylesheets = @getStylesheetPaths().map (stylesheetPath) ->
[stylesheetPath, atom.themes.loadStylesheet(stylesheetPath, true)]
@stylesheets = @getStylesheetPaths().map (stylesheetPath) =>
[stylesheetPath, @themeManager.loadStylesheet(stylesheetPath, true)]
getStylesheetsPath: ->
path.join(@path, 'styles')
@@ -304,7 +274,7 @@ class Package
grammarPaths = fs.listSync(grammarsDirPath, ['json', 'cson'])
for grammarPath in grammarPaths
try
grammar = atom.grammars.readGrammarSync(grammarPath)
grammar = @grammarRegistry.readGrammarSync(grammarPath)
grammar.packageName = @name
grammar.bundledPackage = @bundledPackage
@grammars.push(grammar)
@@ -319,11 +289,11 @@ class Package
return Promise.resolve() if @grammarsLoaded
loadGrammar = (grammarPath, callback) =>
atom.grammars.readGrammar grammarPath, (error, grammar) =>
@grammarRegistry.readGrammar grammarPath, (error, grammar) =>
if error?
detail = "#{error.message} in #{grammarPath}"
stack = "#{error.stack}\n at #{grammarPath}:1:1"
atom.notifications.addFatalError("Failed to load a #{@name} package grammar", {stack, detail, dismissable: true})
@notificationManager.addFatalError("Failed to load a #{@name} package grammar", {stack, detail, dismissable: true})
else
grammar.packageName = @name
grammar.bundledPackage = @bundledPackage
@@ -343,11 +313,11 @@ class Package
@settings = []
loadSettingsFile = (settingsPath, callback) =>
ScopedProperties.load settingsPath, (error, settings) =>
ScopedProperties.load settingsPath, @config, (error, settings) =>
if error?
detail = "#{error.message} in #{settingsPath}"
stack = "#{error.stack}\n at #{settingsPath}:1:1"
atom.notifications.addFatalError("Failed to load the #{@name} package settings", {stack, detail, dismissable: true})
@notificationManager.addFatalError("Failed to load the #{@name} package settings", {stack, detail, dismissable: true})
else
@settings.push(settings)
settings.activate() if @settingsActivated
@@ -370,10 +340,8 @@ class Package
console.error "Error serializing package '#{@name}'", e.stack
deactivate: ->
@rejectActivationPromise?()
@activationPromise = null
@resolveActivationPromise = null
@rejectActivationPromise = null
@activationCommandSubscriptions?.dispose()
@deactivateResources()
@deactivateConfig()
@@ -430,9 +398,9 @@ class Package
return @mainModulePath if @resolvedMainModulePath
@resolvedMainModulePath = true
if @bundledPackage and packagesCache[@name]?
if packagesCache[@name].main
@mainModulePath = "#{atom.packages.resourcePath}#{path.sep}#{packagesCache[@name].main}"
if @bundledPackage and @packageManager.packagesCache[@name]?
if @packageManager.packagesCache[@name].main
@mainModulePath = "#{@packageManager.resourcePath}#{path.sep}#{@packageManager.packagesCache[@name].main}"
else
@mainModulePath = null
else
@@ -466,7 +434,7 @@ class Package
# Add dummy command so it appears in menu.
# The real command will be registered on package activation
try
@activationCommandSubscriptions.add atom.commands.add selector, command, ->
@activationCommandSubscriptions.add @commandRegistry.add selector, command, ->
catch error
if error.code is 'EBADSELECTOR'
metadataPath = path.join(@path, 'package.json')
@@ -474,7 +442,7 @@ class Package
error.stack += "\n at #{metadataPath}:1:1"
throw error
@activationCommandSubscriptions.add atom.commands.onWillDispatch (event) =>
@activationCommandSubscriptions.add @commandRegistry.onWillDispatch (event) =>
return unless event.type is command
currentTarget = event.target
while currentTarget
@@ -505,7 +473,7 @@ class Package
@activationHookSubscriptions = new CompositeDisposable
for hook in @getActivationHooks()
do (hook) =>
@activationHookSubscriptions.add(atom.packages.onDidTriggerActivationHook(hook, => @activateNow())) if hook? and _.isString(hook) and hook.trim().length > 0
@activationHookSubscriptions.add(@packageManager.onDidTriggerActivationHook(hook, => @activateNow())) if hook? and _.isString(hook) and hook.trim().length > 0
return
@@ -567,7 +535,7 @@ class Package
isCompatible: ->
return @compatible if @compatible?
if @path.indexOf(path.join(atom.packages.resourcePath, 'node_modules') + path.sep) is 0
if @path.indexOf(path.join(@packageManager.resourcePath, 'node_modules') + path.sep) is 0
# Bundled packages are always considered compatible
@compatible = true
else if @getMainModulePath()
@@ -603,7 +571,7 @@ class Package
stderr = ''
stdout = ''
new BufferedProcess({
command: atom.packages.getApmPath()
command: @packageManager.getApmPath()
args: ['rebuild', '--no-color']
options: {cwd: @path}
stderr: (output) -> stderr += output
@@ -625,7 +593,7 @@ class Package
# This information is cached in local storage on a per package/version basis
# to minimize the impact on startup time.
getIncompatibleNativeModules: ->
unless atom.inDevMode()
unless @devMode
try
if arrayAsString = global.localStorage.getItem(@getIncompatibleNativeModulesStorageKey())
return JSON.parse(arrayAsString)
@@ -666,4 +634,4 @@ class Package
detail = error.message
stack = error.stack ? error
atom.notifications.addFatalError(message, {stack, detail, dismissable: true})
@notificationManager.addFatalError(message, {stack, detail, dismissable: true})

View File

@@ -8,7 +8,9 @@ class PaneAxisElement extends HTMLElement
detachedCallback: ->
@subscriptions.dispose()
initialize: (@model) ->
initialize: (@model, {@views}) ->
throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views?
@subscriptions.add @model.onDidAddChild(@childAdded.bind(this))
@subscriptions.add @model.onDidRemoveChild(@childRemoved.bind(this))
@subscriptions.add @model.onDidReplaceChild(@childReplaced.bind(this))
@@ -27,7 +29,7 @@ class PaneAxisElement extends HTMLElement
element?.nodeName.toLowerCase() is 'atom-pane-resize-handle'
childAdded: ({child, index}) ->
view = atom.views.getView(child)
view = @views.getView(child)
@insertBefore(view, @children[index * 2])
prevElement = view.previousSibling
@@ -43,7 +45,7 @@ class PaneAxisElement extends HTMLElement
@insertBefore(resizeHandle, nextElement)
childRemoved: ({child}) ->
view = atom.views.getView(child)
view = @views.getView(child)
siblingView = view.previousSibling
# make sure next sibling view is pane resize view
if siblingView? and @isPaneResizeHandleElement(siblingView)

View File

@@ -4,19 +4,16 @@ Model = require './model'
module.exports =
class PaneAxis extends Model
atom.deserializers.add(this)
parent: null
container: null
orientation: null
@deserialize: (state, params) ->
container = params?.container
state.container = container
state.children = state.children.map (childState) -> atom.deserializers.deserialize(childState, {container})
@deserialize: (state, {deserializers}) ->
state.children = state.children.map (childState) ->
deserializers.deserialize(childState)
new this(state)
constructor: ({@container, @orientation, children, flexScale}={}) ->
constructor: ({@orientation, children, flexScale}={}) ->
@emitter = new Emitter
@subscriptionsByChild = new WeakMap
@subscriptions = new CompositeDisposable
@@ -43,7 +40,10 @@ class PaneAxis extends Model
getContainer: -> @container
setContainer: (@container) -> @container
setContainer: (container) ->
if container and container isnt @container
@container = container
child.setContainer(container) for child in @children
getOrientation: -> @orientation

View File

@@ -7,7 +7,9 @@ class PaneContainerElement extends HTMLElement
@subscriptions = new CompositeDisposable
@classList.add 'panes'
initialize: (@model) ->
initialize: (@model, {@views}) ->
throw new Error("Must pass a views parameter when initializing PaneContainerElements") unless @views?
@subscriptions.add @model.observeRoot(@rootChanged.bind(this))
this
@@ -15,7 +17,7 @@ class PaneContainerElement extends HTMLElement
focusedElement = document.activeElement if @hasFocus()
@firstChild?.remove()
if root?
view = atom.views.getView(root)
view = @views.getView(root)
@appendChild(view)
focusedElement?.focus()
@@ -40,7 +42,7 @@ class PaneContainerElement extends HTMLElement
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
paneView = atom.views.getView(@model.getActivePane())
paneView = @views.getView(@model.getActivePane())
box = @boundingBoxForPaneView(paneView)
paneViews = _.toArray(@querySelectorAll('atom-pane'))

View File

@@ -7,46 +7,37 @@ ItemRegistry = require './item-registry'
module.exports =
class PaneContainer extends Model
atom.deserializers.add(this)
@version: 1
serializationVersion: 1
root: null
stoppedChangingActivePaneItemDelay: 100
stoppedChangingActivePaneItemTimeout: null
@deserialize: (state) ->
container = Object.create(@prototype) # allows us to pass a self reference to our child before invoking constructor
state.root = atom.deserializers.deserialize(state.root, {container})
state.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes')
state.activePane = find state.root.getPanes(), (pane) -> pane.id is state.activePaneId
@call(container, state) # run constructor
container
constructor: (params) ->
super
@activePane = params?.activePane
{@config, applicationDelegate, notificationManager, deserializerManager} = params
@emitter = new Emitter
@subscriptions = new CompositeDisposable
@itemRegistry = new ItemRegistry
@setRoot(params?.root ? new Pane)
@setActivePane(@getPanes()[0]) unless @getActivePane()
@destroyEmptyPanes() if params?.destroyEmptyPanes
@setRoot(new Pane({container: this, @config, applicationDelegate, notificationManager, deserializerManager}))
@setActivePane(@getRoot())
@monitorActivePaneItem()
@monitorPaneItems()
serialize: (params) ->
deserializer: 'PaneContainer'
version: @constructor.version
version: @serializationVersion
root: @root?.serialize()
activePaneId: @activePane.id
deserialize: (state, deserializerManager) ->
return unless state.version is @serializationVersion
@setRoot(deserializerManager.deserialize(state.root))
activePane = find @getRoot().getPanes(), (pane) -> pane.id is state.activePaneId
@setActivePane(activePane ? @getPanes()[0])
@destroyEmptyPanes() if @config.get('core.destroyEmptyPanes')
onDidChangeRoot: (fn) ->
@emitter.on 'did-change-root', fn

View File

@@ -44,14 +44,17 @@ class PaneElement extends HTMLElement
event.stopPropagation()
@getModel().activate()
pathsToOpen = Array::map.call event.dataTransfer.files, (file) -> file.path
atom.open({pathsToOpen}) if pathsToOpen.length > 0
@open({pathsToOpen}) if pathsToOpen.length > 0
@addEventListener 'focus', handleFocus, true
@addEventListener 'blur', handleBlur, true
@addEventListener 'dragover', handleDragOver
@addEventListener 'drop', handleDrop
initialize: (@model) ->
initialize: (@model, {@views, @open}) ->
throw new Error("Must pass a views parameter when initializing PaneElements") unless @views?
throw new Error("Must pass a open parameter when initializing PaneElements") unless @open?
@subscriptions.add @model.onDidActivate(@activated.bind(this))
@subscriptions.add @model.observeActive(@activeStatusChanged.bind(this))
@subscriptions.add @model.observeActiveItem(@activeItemChanged.bind(this))
@@ -78,7 +81,7 @@ class PaneElement extends HTMLElement
return unless item?
hasFocus = @hasFocus()
itemView = atom.views.getView(item)
itemView = @views.getView(item)
if itemPath = item.getPath?()
@dataset.activeItemName = path.basename(itemPath)
@@ -109,7 +112,7 @@ class PaneElement extends HTMLElement
itemView.style.display = 'none'
itemRemoved: ({item, index, destroyed}) ->
if viewToRemove = atom.views.getView(item)
if viewToRemove = @views.getView(item)
viewToRemove.remove()
paneDestroyed: ->
@@ -118,35 +121,9 @@ class PaneElement extends HTMLElement
flexScaleChanged: (flexScale) ->
@style.flexGrow = flexScale
getActiveView: -> atom.views.getView(@model.getActiveItem())
getActiveView: -> @views.getView(@model.getActiveItem())
hasFocus: ->
this is document.activeElement or @contains(document.activeElement)
atom.commands.add 'atom-workspace',
'pane:show-next-item': -> @getModel().getActivePane().activateNextItem()
'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem()
'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0)
'pane:show-item-2': -> @getModel().getActivePane().activateItemAtIndex(1)
'pane:show-item-3': -> @getModel().getActivePane().activateItemAtIndex(2)
'pane:show-item-4': -> @getModel().getActivePane().activateItemAtIndex(3)
'pane:show-item-5': -> @getModel().getActivePane().activateItemAtIndex(4)
'pane:show-item-6': -> @getModel().getActivePane().activateItemAtIndex(5)
'pane:show-item-7': -> @getModel().getActivePane().activateItemAtIndex(6)
'pane:show-item-8': -> @getModel().getActivePane().activateItemAtIndex(7)
'pane:show-item-9': -> @getModel().getActivePane().activateItemAtIndex(8)
'pane:move-item-right': -> @getModel().getActivePane().moveItemRight()
'pane:move-item-left': -> @getModel().getActivePane().moveItemLeft()
atom.commands.add 'atom-pane',
'pane:save-items': -> @getModel().saveItems()
'pane:split-left': -> @getModel().splitLeft(copyActiveItem: true)
'pane:split-right': -> @getModel().splitRight(copyActiveItem: true)
'pane:split-up': -> @getModel().splitUp(copyActiveItem: true)
'pane:split-down': -> @getModel().splitDown(copyActiveItem: true)
'pane:close': -> @getModel().close()
'pane:close-other-items': -> @getModel().destroyInactiveItems()
'pane:increase-size': -> @getModel().increaseSize()
'pane:decrease-size': -> @getModel().decreaseSize()
module.exports = PaneElement = document.registerElement 'atom-pane', prototype: PaneElement.prototype

View File

@@ -10,30 +10,31 @@ TextEditor = require './text-editor'
# the default configuration, tabs are also displayed for each item.
module.exports =
class Pane extends Model
atom.deserializers.add(this)
container: undefined
activeItem: undefined
focused: false
@deserialize: (state, params) ->
@deserialize: (state, {deserializers, applicationDelegate, config, notifications}) ->
{items, activeItemURI, activeItemUri} = state
state.container = params?.container
activeItemURI ?= activeItemUri
state.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState))
state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState))
state.activeItem = find state.items, (item) ->
if typeof item.getURI is 'function'
itemURI = item.getURI()
itemURI is activeItemURI
new this(state)
new Pane(extend(state, {
deserializerManager: deserializers,
notificationManager: notifications,
config, applicationDelegate
}))
constructor: (params) ->
super
@container = params?.container
@activeItem = params?.activeItem
@focused = params?.focused
{
@activeItem, @focused, @applicationDelegate, @notificationManager, @config,
@deserializerManager
} = params
@emitter = new Emitter
@itemSubscriptions = new WeakMap
@@ -61,7 +62,7 @@ class Pane extends Model
getContainer: -> @container
setContainer: (container) ->
unless container is @container
if container and container isnt @container
@container = container
container.didAddPane({pane: this})
@@ -395,7 +396,7 @@ class Pane extends Model
@items.splice(index, 1)
@emitter.emit 'did-remove-item', {item, index, destroyed: not moved, moved}
@container?.didDestroyPaneItem({item, index, pane: this}) unless moved
@destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes')
@destroy() if @items.length is 0 and @config.get('core.destroyEmptyPanes')
# Public: Move the given item to the given index.
#
@@ -461,7 +462,7 @@ class Pane extends Model
else
return true
chosen = atom.confirm
chosen = @applicationDelegate.confirm
message: "'#{item.getTitle?() ? uri}' 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"]
@@ -514,7 +515,7 @@ class Pane extends Model
saveOptions = item.getSaveDialogOptions?() ? {}
saveOptions.defaultPath ?= item.getPath()
newItemPath = atom.showSaveDialogSync(saveOptions)
newItemPath = @applicationDelegate.showSaveDialog(saveOptions)
if newItemPath
try
item.saveAs(newItemPath)
@@ -554,7 +555,7 @@ class Pane extends Model
copyActiveItem: ->
if @activeItem?
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
@activeItem.copy?() ? @deserializerManager.deserialize(@activeItem.serialize())
###
Section: Lifecycle
@@ -646,7 +647,7 @@ class Pane extends Model
@parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this], @flexScale}))
@setFlexScale(1)
newPane = new @constructor(params)
newPane = new Pane(extend({@applicationDelegate, @deserializerManager, @config}, params))
switch side
when 'before' then @parent.insertChildBefore(this, newPane)
when 'after' then @parent.insertChildAfter(this, newPane)
@@ -688,12 +689,12 @@ class Pane extends Model
handleSaveError: (error, item) ->
itemPath = error.path ? item?.getPath?()
addWarningWithPath = (message, options) ->
addWarningWithPath = (message, options) =>
message = "#{message} '#{itemPath}'" if itemPath
atom.notifications.addWarning(message, options)
@notificationManager.addWarning(message, options)
if error.code is 'EISDIR' or error.message?.endsWith?('is a directory')
atom.notifications.addWarning("Unable to save file: #{error.message}")
@notificationManager.addWarning("Unable to save file: #{error.message}")
else if error.code is 'EACCES'
addWarningWithPath('Unable to save file: Permission denied')
else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST']
@@ -716,6 +717,6 @@ class Pane extends Model
addWarningWithPath('Unable to save file: Invalid seek')
else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message)
fileName = errorMatch[1]
atom.notifications.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to")
@notificationManager.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to")
else
throw error

View File

@@ -4,7 +4,9 @@ class PanelContainerElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
initialize: (@model) ->
initialize: (@model, {@views}) ->
throw new Error("Must pass a views parameter when initializing PanelContainerElements") unless @views?
@subscriptions.add @model.onDidAddPanel(@panelAdded.bind(this))
@subscriptions.add @model.onDidRemovePanel(@panelRemoved.bind(this))
@subscriptions.add @model.onDidDestroy(@destroyed.bind(this))
@@ -14,7 +16,7 @@ class PanelContainerElement extends HTMLElement
getModel: -> @model
panelAdded: ({panel, index}) ->
panelElement = atom.views.getView(panel)
panelElement = @views.getView(panel)
panelElement.classList.add(@model.getLocation())
if @model.isModal()
panelElement.classList.add("overlay", "from-top")
@@ -33,7 +35,7 @@ class PanelContainerElement extends HTMLElement
@hideAllPanelsExcept(panel) if visible
panelRemoved: ({panel, index}) ->
@removeChild(atom.views.getView(panel))
@removeChild(@views.getView(panel))
destroyed: ->
@subscriptions.dispose()

View File

@@ -5,7 +5,9 @@ class PanelElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
initialize: (@model) ->
initialize: (@model, {@views}) ->
throw new Error("Must pass a views parameter when initializing PanelElements") unless @views?
@appendChild(@getItemView())
@classList.add(@model.getClassName().split(' ')...) if @model.getClassName()?
@@ -17,7 +19,7 @@ class PanelElement extends HTMLElement
@model ?= new Panel
getItemView: ->
atom.views.getView(@getModel().getItem())
@views.getView(@getModel().getItem())
attachedCallback: ->
@visibleChanged(@getModel().isVisible())

View File

@@ -3,7 +3,7 @@ url = require 'url'
_ = require 'underscore-plus'
fs = require 'fs-plus'
{Emitter} = require 'event-kit'
{Emitter, Disposable} = require 'event-kit'
TextBuffer = require 'text-buffer'
DefaultDirectoryProvider = require './default-directory-provider'
@@ -17,68 +17,35 @@ GitRepositoryProvider = require './git-repository-provider'
# An instance of this class is always available as the `atom.project` global.
module.exports =
class Project extends Model
atom.deserializers.add(this)
###
Section: Construction and Destruction
###
@deserialize: (state) ->
state.buffers = _.compact state.buffers.map (bufferState) ->
# Check that buffer's file path is accessible
return if fs.isDirectorySync(bufferState.filePath)
if bufferState.filePath
try
fs.closeSync(fs.openSync(bufferState.filePath, 'r'))
catch error
return unless error.code is 'ENOENT'
atom.deserializers.deserialize(bufferState)
new this(state)
constructor: ({path, paths, @buffers}={}) ->
constructor: ({@notificationManager, packageManager, config}) ->
@emitter = new Emitter
@buffers ?= []
@buffers = []
@paths = []
@rootDirectories = []
@repositories = []
@directoryProviders = []
@defaultDirectoryProvider = new DefaultDirectoryProvider()
atom.packages.serviceHub.consume(
'atom.directory-provider',
'^0.1.0',
(provider) => @directoryProviders.unshift(provider))
# Mapping from the real path of a {Directory} to a {Promise} that resolves
# to either a {Repository} or null. Ideally, the {Directory} would be used
# as the key; however, there can be multiple {Directory} objects created for
# the same real path, so it is not a good key.
@repositoryPromisesByPath = new Map()
@repositoryProviders = [new GitRepositoryProvider(this)]
atom.packages.serviceHub.consume(
'atom.repository-provider',
'^0.1.0',
(provider) =>
@repositoryProviders.push(provider)
# If a path in getPaths() does not have a corresponding Repository, try
# to assign one by running through setPaths() again now that
# @repositoryProviders has been updated.
if null in @repositories
@setPaths(@getPaths())
)
@subscribeToBuffer(buffer) for buffer in @buffers
paths ?= _.compact([path])
@setPaths(paths)
@repositoryProviders = [new GitRepositoryProvider(this, config)]
@consumeServices(packageManager)
destroyed: ->
buffer.destroy() for buffer in @getBuffers()
buffer.destroy() for buffer in @buffers
@setPaths([])
reset: (packageManager) ->
@emitter.dispose()
@emitter = new Emitter
buffer?.destroy() for buffer in @buffers
@buffers = []
@setPaths([])
@consumeServices(packageManager)
destroyUnretainedBuffers: ->
buffer.destroy() for buffer in @getBuffers() when not buffer.isRetained()
return
@@ -87,6 +54,22 @@ class Project extends Model
Section: Serialization
###
deserialize: (state, deserializerManager) ->
states.paths = [state.path] if state.path? # backward compatibility
@buffers = _.compact state.buffers.map (bufferState) ->
# Check that buffer's file path is accessible
return if fs.isDirectorySync(bufferState.filePath)
if bufferState.filePath
try
fs.closeSync(fs.openSync(bufferState.filePath, 'r'))
catch error
return unless error.code is 'ENOENT'
deserializerManager.deserialize(bufferState)
@subscribeToBuffer(buffer) for buffer in @buffers
@setPaths(state.paths)
serialize: ->
deserializer: 'Project'
paths: @getPaths()
@@ -291,39 +274,25 @@ class Project extends Model
Section: Private
###
# Given a path to a file, this constructs and associates a new
# {TextEditor}, showing the file.
#
# * `filePath` The {String} path of the file to associate with.
# * `options` Options that you can pass to the {TextEditor} constructor.
#
# Returns a promise that resolves to an {TextEditor}.
open: (filePath, options={}) ->
filePath = @resolvePath(filePath)
consumeServices: ({serviceHub}) ->
serviceHub.consume(
'atom.directory-provider',
'^0.1.0',
(provider) =>
@directoryProviders.unshift(provider)
new Disposable =>
@directoryProviders.splice(@directoryProviders.indexOf(provider), 1)
)
if filePath?
try
fs.closeSync(fs.openSync(filePath, 'r'))
catch error
# allow ENOENT errors to create an editor for paths that dont exist
throw error unless error.code is 'ENOENT'
absoluteFilePath = @resolvePath(filePath)
fileSize = fs.getSizeSync(absoluteFilePath)
if fileSize >= 20 * 1048576 # 20MB
choice = atom.confirm
message: 'Atom will be unresponsive during the loading of very large files.'
detailedMessage: "Do you still want to load this file?"
buttons: ["Proceed", "Cancel"]
if choice is 1
error = new Error
error.code = 'CANCELLED'
throw error
@bufferForPath(absoluteFilePath).then (buffer) =>
@buildEditorForBuffer(buffer, _.extend({fileSize}, options))
serviceHub.consume(
'atom.repository-provider',
'^0.1.0',
(provider) =>
@repositoryProviders.push(provider)
@setPaths(@getPaths()) if null in @repositories
new Disposable =>
@repositoryProviders.splice(@repositoryProviders.indexOf(provider), 1)
)
# Retrieves all the {TextBuffer}s in the project; that is, the
# buffers for all open files.
@@ -404,11 +373,6 @@ class Project extends Model
[buffer] = @buffers.splice(index, 1)
buffer?.destroy()
buildEditorForBuffer: (buffer, editorOptions) ->
largeFileMode = editorOptions.fileSize >= 2 * 1048576 # 2MB
editor = new TextEditor(_.extend({buffer, largeFileMode, registerEditor: true}, editorOptions))
editor
eachBuffer: (args...) ->
subscriber = args.shift() if args.length > 1
callback = args.shift()
@@ -421,9 +385,9 @@ class Project extends Model
subscribeToBuffer: (buffer) ->
buffer.onDidDestroy => @removeBuffer(buffer)
buffer.onWillThrowWatchError ({error, handle}) ->
buffer.onWillThrowWatchError ({error, handle}) =>
handle()
atom.notifications.addWarning """
@notificationManager.addWarning """
Unable to read file after file `#{error.eventType}` event.
Make sure you have permission to access `#{buffer.getPath()}`.
""",

View File

@@ -0,0 +1,212 @@
ipc = require 'ipc'
module.exports = ({commandRegistry, commandInstaller, config}) ->
commandRegistry.add 'atom-workspace',
'pane:show-next-item': -> @getModel().getActivePane().activateNextItem()
'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem()
'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0)
'pane:show-item-2': -> @getModel().getActivePane().activateItemAtIndex(1)
'pane:show-item-3': -> @getModel().getActivePane().activateItemAtIndex(2)
'pane:show-item-4': -> @getModel().getActivePane().activateItemAtIndex(3)
'pane:show-item-5': -> @getModel().getActivePane().activateItemAtIndex(4)
'pane:show-item-6': -> @getModel().getActivePane().activateItemAtIndex(5)
'pane:show-item-7': -> @getModel().getActivePane().activateItemAtIndex(6)
'pane:show-item-8': -> @getModel().getActivePane().activateItemAtIndex(7)
'pane:show-item-9': -> @getModel().getActivePane().activateItemAtIndex(8)
'pane:move-item-right': -> @getModel().getActivePane().moveItemRight()
'pane:move-item-left': -> @getModel().getActivePane().moveItemLeft()
'window:increase-font-size': -> @getModel().increaseFontSize()
'window:decrease-font-size': -> @getModel().decreaseFontSize()
'window:reset-font-size': -> @getModel().resetFontSize()
'application:about': -> ipc.send('command', 'application:about')
'application:show-preferences': -> ipc.send('command', 'application:show-settings')
'application:show-settings': -> ipc.send('command', 'application:show-settings')
'application:quit': -> ipc.send('command', 'application:quit')
'application:hide': -> ipc.send('command', 'application:hide')
'application:hide-other-applications': -> ipc.send('command', 'application:hide-other-applications')
'application:install-update': -> ipc.send('command', 'application:install-update')
'application:unhide-all-applications': -> ipc.send('command', 'application:unhide-all-applications')
'application:new-window': -> ipc.send('command', 'application:new-window')
'application:new-file': -> ipc.send('command', 'application:new-file')
'application:open': -> ipc.send('command', 'application:open')
'application:open-file': -> ipc.send('command', 'application:open-file')
'application:open-folder': -> ipc.send('command', 'application:open-folder')
'application:open-dev': -> ipc.send('command', 'application:open-dev')
'application:open-safe': -> ipc.send('command', 'application:open-safe')
'application:add-project-folder': -> atom.addProjectFolder()
'application:minimize': -> ipc.send('command', 'application:minimize')
'application:zoom': -> ipc.send('command', 'application:zoom')
'application:bring-all-windows-to-front': -> ipc.send('command', 'application:bring-all-windows-to-front')
'application:open-your-config': -> ipc.send('command', 'application:open-your-config')
'application:open-your-init-script': -> ipc.send('command', 'application:open-your-init-script')
'application:open-your-keymap': -> ipc.send('command', 'application:open-your-keymap')
'application:open-your-snippets': -> ipc.send('command', 'application:open-your-snippets')
'application:open-your-stylesheet': -> ipc.send('command', 'application:open-your-stylesheet')
'application:open-license': -> @getModel().openLicense()
'window:run-package-specs': -> @runPackageSpecs()
'window:focus-next-pane': -> @getModel().activateNextPane()
'window:focus-previous-pane': -> @getModel().activatePreviousPane()
'window:focus-pane-above': -> @focusPaneViewAbove()
'window:focus-pane-below': -> @focusPaneViewBelow()
'window:focus-pane-on-left': -> @focusPaneViewOnLeft()
'window:focus-pane-on-right': -> @focusPaneViewOnRight()
'window:save-all': -> @getModel().saveAll()
'window:toggle-invisibles': -> config.set("editor.showInvisibles", not config.get("editor.showInvisibles"))
'window:log-deprecation-warnings': -> Grim.logDeprecations()
'window:toggle-auto-indent': -> config.set("editor.autoIndent", not config.get("editor.autoIndent"))
'pane:reopen-closed-item': -> @getModel().reopenItem()
'core:close': -> @getModel().destroyActivePaneItemOrEmptyPane()
'core:save': -> @getModel().saveActivePaneItem()
'core:save-as': -> @getModel().saveActivePaneItemAs()
if process.platform is 'darwin'
commandRegistry.add 'atom-workspace', 'window:install-shell-commands', ->
commandInstaller.installShellCommandsInteractively()
commandRegistry.add 'atom-pane',
'pane:save-items': -> @getModel().saveItems()
'pane:split-left': -> @getModel().splitLeft(copyActiveItem: true)
'pane:split-right': -> @getModel().splitRight(copyActiveItem: true)
'pane:split-up': -> @getModel().splitUp(copyActiveItem: true)
'pane:split-down': -> @getModel().splitDown(copyActiveItem: true)
'pane:close': -> @getModel().close()
'pane:close-other-items': -> @getModel().destroyInactiveItems()
'pane:increase-size': -> @getModel().increaseSize()
'pane:decrease-size': -> @getModel().decreaseSize()
commandRegistry.add 'atom-text-editor', stopEventPropagation(
'core:undo': -> @undo()
'core:redo': -> @redo()
'core:move-left': -> @moveLeft()
'core:move-right': -> @moveRight()
'core:select-left': -> @selectLeft()
'core:select-right': -> @selectRight()
'core:select-up': -> @selectUp()
'core:select-down': -> @selectDown()
'core:select-all': -> @selectAll()
'editor:select-word': -> @selectWordsContainingCursors()
'editor:consolidate-selections': (event) -> event.abortKeyBinding() unless @consolidateSelections()
'editor:move-to-beginning-of-next-paragraph': -> @moveToBeginningOfNextParagraph()
'editor:move-to-beginning-of-previous-paragraph': -> @moveToBeginningOfPreviousParagraph()
'editor:move-to-beginning-of-screen-line': -> @moveToBeginningOfScreenLine()
'editor:move-to-beginning-of-line': -> @moveToBeginningOfLine()
'editor:move-to-end-of-screen-line': -> @moveToEndOfScreenLine()
'editor:move-to-end-of-line': -> @moveToEndOfLine()
'editor:move-to-first-character-of-line': -> @moveToFirstCharacterOfLine()
'editor:move-to-beginning-of-word': -> @moveToBeginningOfWord()
'editor:move-to-end-of-word': -> @moveToEndOfWord()
'editor:move-to-beginning-of-next-word': -> @moveToBeginningOfNextWord()
'editor:move-to-previous-word-boundary': -> @moveToPreviousWordBoundary()
'editor:move-to-next-word-boundary': -> @moveToNextWordBoundary()
'editor:move-to-previous-subword-boundary': -> @moveToPreviousSubwordBoundary()
'editor:move-to-next-subword-boundary': -> @moveToNextSubwordBoundary()
'editor:select-to-beginning-of-next-paragraph': -> @selectToBeginningOfNextParagraph()
'editor:select-to-beginning-of-previous-paragraph': -> @selectToBeginningOfPreviousParagraph()
'editor:select-to-end-of-line': -> @selectToEndOfLine()
'editor:select-to-beginning-of-line': -> @selectToBeginningOfLine()
'editor:select-to-end-of-word': -> @selectToEndOfWord()
'editor:select-to-beginning-of-word': -> @selectToBeginningOfWord()
'editor:select-to-beginning-of-next-word': -> @selectToBeginningOfNextWord()
'editor:select-to-next-word-boundary': -> @selectToNextWordBoundary()
'editor:select-to-previous-word-boundary': -> @selectToPreviousWordBoundary()
'editor:select-to-next-subword-boundary': -> @selectToNextSubwordBoundary()
'editor:select-to-previous-subword-boundary': -> @selectToPreviousSubwordBoundary()
'editor:select-to-first-character-of-line': -> @selectToFirstCharacterOfLine()
'editor:select-line': -> @selectLinesContainingCursors()
)
commandRegistry.add 'atom-text-editor', stopEventPropagationAndGroupUndo(config,
'core:backspace': -> @backspace()
'core:delete': -> @delete()
'core:cut': -> @cutSelectedText()
'core:copy': -> @copySelectedText()
'core:paste': -> @pasteText()
'editor:delete-to-previous-word-boundary': -> @deleteToPreviousWordBoundary()
'editor:delete-to-next-word-boundary': -> @deleteToNextWordBoundary()
'editor:delete-to-beginning-of-word': -> @deleteToBeginningOfWord()
'editor:delete-to-beginning-of-line': -> @deleteToBeginningOfLine()
'editor:delete-to-end-of-line': -> @deleteToEndOfLine()
'editor:delete-to-end-of-word': -> @deleteToEndOfWord()
'editor:delete-to-beginning-of-subword': -> @deleteToBeginningOfSubword()
'editor:delete-to-end-of-subword': -> @deleteToEndOfSubword()
'editor:delete-line': -> @deleteLine()
'editor:cut-to-end-of-line': -> @cutToEndOfLine()
'editor:cut-to-end-of-buffer-line': -> @cutToEndOfBufferLine()
'editor:transpose': -> @transpose()
'editor:upper-case': -> @upperCase()
'editor:lower-case': -> @lowerCase()
'editor:copy-selection': -> @copyOnlySelectedText()
)
commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagation(
'core:move-up': -> @moveUp()
'core:move-down': -> @moveDown()
'core:move-to-top': -> @moveToTop()
'core:move-to-bottom': -> @moveToBottom()
'core:page-up': -> @pageUp()
'core:page-down': -> @pageDown()
'core:select-to-top': -> @selectToTop()
'core:select-to-bottom': -> @selectToBottom()
'core:select-page-up': -> @selectPageUp()
'core:select-page-down': -> @selectPageDown()
'editor:add-selection-below': -> @addSelectionBelow()
'editor:add-selection-above': -> @addSelectionAbove()
'editor:split-selections-into-lines': -> @splitSelectionsIntoLines()
'editor:toggle-soft-tabs': -> @toggleSoftTabs()
'editor:toggle-soft-wrap': -> @toggleSoftWrapped()
'editor:fold-all': -> @foldAll()
'editor:unfold-all': -> @unfoldAll()
'editor:fold-current-row': -> @foldCurrentRow()
'editor:unfold-current-row': -> @unfoldCurrentRow()
'editor:fold-selection': -> @foldSelectedLines()
'editor:fold-at-indent-level-1': -> @foldAllAtIndentLevel(0)
'editor:fold-at-indent-level-2': -> @foldAllAtIndentLevel(1)
'editor:fold-at-indent-level-3': -> @foldAllAtIndentLevel(2)
'editor:fold-at-indent-level-4': -> @foldAllAtIndentLevel(3)
'editor:fold-at-indent-level-5': -> @foldAllAtIndentLevel(4)
'editor:fold-at-indent-level-6': -> @foldAllAtIndentLevel(5)
'editor:fold-at-indent-level-7': -> @foldAllAtIndentLevel(6)
'editor:fold-at-indent-level-8': -> @foldAllAtIndentLevel(7)
'editor:fold-at-indent-level-9': -> @foldAllAtIndentLevel(8)
'editor:log-cursor-scope': -> @logCursorScope()
'editor:copy-path': -> @copyPathToClipboard()
'editor:toggle-indent-guide': -> config.set('editor.showIndentGuide', not config.get('editor.showIndentGuide'))
'editor:toggle-line-numbers': -> config.set('editor.showLineNumbers', not config.get('editor.showLineNumbers'))
'editor:scroll-to-cursor': -> @scrollToCursorPosition()
)
commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(config,
'editor:indent': -> @indent()
'editor:auto-indent': -> @autoIndentSelectedRows()
'editor:indent-selected-rows': -> @indentSelectedRows()
'editor:outdent-selected-rows': -> @outdentSelectedRows()
'editor:newline': -> @insertNewline()
'editor:newline-below': -> @insertNewlineBelow()
'editor:newline-above': -> @insertNewlineAbove()
'editor:toggle-line-comments': -> @toggleLineCommentsInSelection()
'editor:checkout-head-revision': -> @checkoutHeadRevision()
'editor:move-line-up': -> @moveLineUp()
'editor:move-line-down': -> @moveLineDown()
'editor:duplicate-lines': -> @duplicateLines()
'editor:join-lines': -> @joinLines()
)
stopEventPropagation = (commandListeners) ->
newCommandListeners = {}
for commandName, commandListener of commandListeners
do (commandListener) ->
newCommandListeners[commandName] = (event) ->
event.stopPropagation()
commandListener.call(@getModel(), event)
newCommandListeners
stopEventPropagationAndGroupUndo = (config, commandListeners) ->
newCommandListeners = {}
for commandName, commandListener of commandListeners
do (commandListener) ->
newCommandListeners[commandName] = (event) ->
event.stopPropagation()
model = @getModel()
model.transact config.get('editor.undoGroupingInterval'), ->
commandListener.call(model, event)
newCommandListeners

View File

@@ -3,21 +3,21 @@ CSON = require 'season'
module.exports =
class ScopedProperties
@load: (scopedPropertiesPath, callback) ->
@load: (scopedPropertiesPath, config, callback) ->
CSON.readFile scopedPropertiesPath, (error, scopedProperties={}) ->
if error?
callback(error)
else
callback(null, new ScopedProperties(scopedPropertiesPath, scopedProperties))
callback(null, new ScopedProperties(scopedPropertiesPath, scopedProperties, config))
constructor: (@path, @scopedProperties) ->
constructor: (@path, @scopedProperties, @config) ->
activate: ->
for selector, properties of @scopedProperties
atom.config.set(null, properties, scopeSelector: selector, source: @path)
@config.set(null, properties, scopeSelector: selector, source: @path)
return
deactivate: ->
for selector of @scopedProperties
atom.config.unset(null, scopeSelector: selector, source: @path)
@config.unset(null, scopeSelector: selector, source: @path)
return

View File

@@ -14,7 +14,7 @@ class Selection extends Model
initialScreenRange: null
wordwise: false
constructor: ({@cursor, @marker, @editor, id}) ->
constructor: ({@cursor, @marker, @editor, id, @clipboard}) ->
@emitter = new Emitter
@assignId(id)
@@ -611,7 +611,7 @@ class Selection extends Model
startLevel = @editor.indentLevelForLine(precedingText)
if maintainClipboard
{text: clipboardText, metadata} = atom.clipboard.readWithMetadata()
{text: clipboardText, metadata} = @clipboard.readWithMetadata()
metadata ?= {}
unless metadata.selections?
metadata.selections = [{
@@ -624,9 +624,9 @@ class Selection extends Model
indentBasis: startLevel,
fullLine: fullLine
})
atom.clipboard.write([clipboardText, selectionText].join("\n"), metadata)
@clipboard.write([clipboardText, selectionText].join("\n"), metadata)
else
atom.clipboard.write(selectionText, {
@clipboard.write(selectionText, {
indentBasis: startLevel,
fullLine: fullLine
})

View File

@@ -4,12 +4,16 @@ fs = require "fs-plus"
module.exports =
class StorageFolder
constructor: (containingPath) ->
@path = path.join(containingPath, "storage")
@path = path.join(containingPath, "storage") if containingPath?
store: (name, object) ->
return unless @path?
fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8')
load: (name) ->
return unless @path?
statePath = @pathForKey(name)
try
stateString = fs.readFileSync(statePath, 'utf8')

View File

@@ -1,6 +1,7 @@
fs = require 'fs-plus'
path = require 'path'
{Emitter, Disposable} = require 'event-kit'
StylesElement = require './styles-element'
# Extended: A singleton instance of this class available via `atom.styles`,
# which you can use to globally query and observe the set of active style
@@ -9,7 +10,7 @@ path = require 'path'
# which clone and attach style elements in different contexts.
module.exports =
class StyleManager
constructor: ->
constructor: ({@configDirPath}) ->
@emitter = new Emitter
@styleElements = []
@styleElementsBySourcePath = {}
@@ -154,6 +155,11 @@ class StyleManager
return
buildStylesElement: ->
stylesElement = new StylesElement
stylesElement.initialize(this)
stylesElement
###
Section: Paths
###
@@ -162,8 +168,10 @@ class StyleManager
#
# Returns a {String}.
getUserStyleSheetPath: ->
stylesheetPath = fs.resolve(path.join(atom.getConfigDirPath(), 'styles'), ['css', 'less'])
return "" unless @configDirPath?
stylesheetPath = fs.resolve(path.join(@configDirPath, 'styles'), ['css', 'less'])
if fs.isFileSync(stylesheetPath)
stylesheetPath
else
path.join(atom.getConfigDirPath(), 'styles.less')
path.join(@configDirPath, 'styles.less')

View File

@@ -14,6 +14,7 @@ class StylesElement extends HTMLElement
@emitter.on 'did-update-style-element', callback
createdCallback: ->
@subscriptions = new CompositeDisposable
@emitter = new Emitter
@styleElementClonesByOriginalElement = new WeakMap
@@ -21,31 +22,29 @@ class StylesElement extends HTMLElement
if @context is 'atom-text-editor'
for styleElement in @children
@upgradeDeprecatedSelectors(styleElement)
@initialize()
@context = @getAttribute('context') ? undefined
detachedCallback: ->
@subscriptions.dispose()
@subscriptions = null
@subscriptions = new CompositeDisposable
attributeChangedCallback: (attrName, oldVal, newVal) ->
@contextChanged() if attrName is 'context'
initialize: ->
return if @subscriptions?
initialize: (@styleManager) ->
throw new Error("Must pass a styleManager parameter when initializing a StylesElement") unless @styleManager?
@subscriptions = new CompositeDisposable
@context = @getAttribute('context') ? undefined
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
@subscriptions.add @styleManager.observeStyleElements(@styleElementAdded.bind(this))
@subscriptions.add @styleManager.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
@subscriptions.add @styleManager.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
contextChanged: ->
return unless @subscriptions?
@styleElementRemoved(child) for child in Array::slice.call(@children)
@context = @getAttribute('context')
@styleElementAdded(styleElement) for styleElement in atom.styles.getStyleElements()
@styleElementAdded(styleElement) for styleElement in @styleManager.getStyleElements()
return
styleElementAdded: (styleElement) ->

Some files were not shown because too many files have changed in this diff Show More