diff --git a/build/package.json b/build/package.json
index ad483c75f..da11dc97a 100644
--- a/build/package.json
+++ b/build/package.json
@@ -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",
diff --git a/build/tasks/spec-task.coffee b/build/tasks/spec-task.coffee
index b0e695798..27465efdf 100644
--- a/build/tasks/spec-task.coffee
+++ b/build/tasks/spec-task.coffee
@@ -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
diff --git a/exports/atom.coffee b/exports/atom.coffee
index 1ceb60fa9..c3601e1cc 100644
--- a/exports/atom.coffee
+++ b/exports/atom.coffee
@@ -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
diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson
index 87c1eb9da..bf2e43a33 100644
--- a/keymaps/darwin.cson
+++ b/keymaps/darwin.cson
@@ -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'
diff --git a/keymaps/linux.cson b/keymaps/linux.cson
index 9e739521c..28f43a8fc 100644
--- a/keymaps/linux.cson
+++ b/keymaps/linux.cson
@@ -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'
diff --git a/keymaps/win32.cson b/keymaps/win32.cson
index b3643207f..f052a64fc 100644
--- a/keymaps/win32.cson
+++ b/keymaps/win32.cson
@@ -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'
diff --git a/menus/darwin.cson b/menus/darwin.cson
index 27318eb47..6fff290e2 100644
--- a/menus/darwin.cson
+++ b/menus/darwin.cson
@@ -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' }
]
diff --git a/menus/linux.cson b/menus/linux.cson
index 354a5ec96..fa831b4a4 100644
--- a/menus/linux.cson
+++ b/menus/linux.cson
@@ -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' }
]
diff --git a/menus/win32.cson b/menus/win32.cson
index 791e9d68d..04da3d388 100644
--- a/menus/win32.cson
+++ b/menus/win32.cson
@@ -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' }
]
diff --git a/package.json b/package.json
index 9191f39db..4b89dc162 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/spec/atom-spec.coffee b/spec/atom-environment-spec.coffee
similarity index 67%
rename from spec/atom-spec.coffee
rename to spec/atom-environment-spec.coffee
index 31265c9a6..f9dbc02f9 100644
--- a/spec/atom-spec.coffee
+++ b/spec/atom-environment-spec.coffee
@@ -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'
diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee
index 972494ec3..584dc193e 100644
--- a/spec/command-installer-spec.coffee
+++ b/spec/command-installer-spec.coffee
@@ -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')
diff --git a/spec/command-registry-spec.coffee b/spec/command-registry-spec.coffee
index f8194aae6..ecdd42fd6 100644
--- a/spec/command-registry-spec.coffee
+++ b/spec/command-registry-spec.coffee
@@ -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()
diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee
index 7a2e9e5a0..bb9ab89a8 100644
--- a/spec/config-spec.coffee
+++ b/spec/config-spec.coffee
@@ -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
diff --git a/spec/context-menu-manager-spec.coffee b/spec/context-menu-manager-spec.coffee
index 4cf33acc6..ddced8452 100644
--- a/spec/context-menu-manager-spec.coffee
+++ b/spec/context-menu-manager-spec.coffee
@@ -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")
diff --git a/spec/custom-gutter-component-spec.coffee b/spec/custom-gutter-component-spec.coffee
index 4b7d81fba..5504e9c40 100644
--- a/spec/custom-gutter-component-spec.coffee
+++ b/spec/custom-gutter-component-spec.coffee
@@ -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
diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee
index 19eccd58d..3e1178749 100644
--- a/spec/display-buffer-spec.coffee
+++ b/spec/display-buffer-spec.coffee
@@ -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()
diff --git a/spec/git-repository-provider-spec.coffee b/spec/git-repository-provider-spec.coffee
index 13a0724d6..bbbfb4b03 100644
--- a/spec/git-repository-provider-spec.coffee
+++ b/spec/git-repository-provider-spec.coffee
@@ -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 =
diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee
index a72cd47cd..31ac176f7 100644
--- a/spec/git-spec.coffee
+++ b/spec/git-spec.coffee
@@ -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 ->
diff --git a/spec/grammars-spec.coffee b/spec/grammars-spec.coffee
index 960ce7d52..82a29892a 100644
--- a/spec/grammars-spec.coffee
+++ b/spec/grammars-spec.coffee
@@ -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", ->
diff --git a/spec/gutter-container-component-spec.coffee b/spec/gutter-container-component-spec.coffee
index 595067de5..73a9d0f6c 100644
--- a/spec/gutter-container-component-spec.coffee
+++ b/spec/gutter-container-component-spec.coffee
@@ -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
diff --git a/spec/integration/helpers/start-atom.coffee b/spec/integration/helpers/start-atom.coffee
index 6a7144141..b8768f532 100644
--- a/spec/integration/helpers/start-atom.coffee
+++ b/spec/integration/helpers/start-atom.coffee
@@ -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)
diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee
index 74dd80f9e..36342470d 100644
--- a/spec/integration/startup-spec.coffee
+++ b/spec/integration/startup-spec.coffee
@@ -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])
diff --git a/spec/jasmine-helper.coffee b/spec/jasmine-helper.coffee
deleted file mode 100644
index 74e4a0384..000000000
--- a/spec/jasmine-helper.coffee
+++ /dev/null
@@ -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
diff --git a/spec/jasmine-test-runner.coffee b/spec/jasmine-test-runner.coffee
new file mode 100644
index 000000000..7ef34ce54
--- /dev/null
+++ b/spec/jasmine-test-runner.coffee
@@ -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)
diff --git a/spec/lines-yardstick-spec.coffee b/spec/lines-yardstick-spec.coffee
index 6320cc99f..1f5a01560 100644
--- a/spec/lines-yardstick-spec.coffee
+++ b/spec/lines-yardstick-spec.coffee
@@ -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
diff --git a/spec/menu-manager-spec.coffee b/spec/menu-manager-spec.coffee
index c047bbcfc..a2d76c1d0 100644
--- a/spec/menu-manager-spec.coffee
+++ b/spec/menu-manager-spec.coffee
@@ -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()
diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee
index d1afd6ade..9a2edee0a 100644
--- a/spec/package-manager-spec.coffee
+++ b/spec/package-manager-spec.coffee
@@ -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", ->
diff --git a/spec/package-spec.coffee b/spec/package-spec.coffee
index bf059e0b5..63a80a7db 100644
--- a/spec/package-spec.coffee
+++ b/spec/package-spec.coffee
@@ -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'
diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee
index 890baec38..98cfffd56 100644
--- a/spec/pane-container-element-spec.coffee
+++ b/spec/pane-container-element-spec.coffee
@@ -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')])
diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee
index d0f2913aa..b94815e45 100644
--- a/spec/pane-container-spec.coffee
+++ b/spec/pane-container-spec.coffee
@@ -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()
diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee
index 40712ce90..3fcdc4ffb 100644
--- a/spec/pane-element-spec.coffee
+++ b/spec/pane-element-spec.coffee
@@ -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()
diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee
index d293a6d2c..0c7a22b77 100644
--- a/spec/pane-spec.coffee
+++ b/spec/pane-spec.coffee
@@ -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
diff --git a/spec/panel-container-element-spec.coffee b/spec/panel-container-element-spec.coffee
index bd4ed6bd9..65577221a 100644
--- a/spec/panel-container-element-spec.coffee
+++ b/spec/panel-container-element-spec.coffee
@@ -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)
diff --git a/spec/panel-element-spec.coffee b/spec/panel-element-spec.coffee
index 4c166c617..73108e25d 100644
--- a/spec/panel-element-spec.coffee
+++ b/spec/panel-element-spec.coffee
@@ -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)
diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee
index 13dd3e70c..2c88aad50 100644
--- a/spec/project-spec.coffee
+++ b/spec/project-spec.coffee
@@ -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')
diff --git a/spec/random-editor-spec.coffee b/spec/random-editor-spec.coffee
index d235ebc25..3924a8412 100644
--- a/spec/random-editor-spec.coffee
+++ b/spec/random-editor-spec.coffee
@@ -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())
diff --git a/spec/sample-with-comments.js b/spec/sample-with-comments.js
new file mode 100644
index 000000000..66dc9051d
--- /dev/null
+++ b/spec/sample-with-comments.js
@@ -0,0 +1 @@
+undefined
\ No newline at end of file
diff --git a/spec/sample.js b/spec/sample.js
new file mode 100644
index 000000000..66dc9051d
--- /dev/null
+++ b/spec/sample.js
@@ -0,0 +1 @@
+undefined
\ No newline at end of file
diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee
index e81f7906f..ec40e32cc 100644
--- a/spec/selection-spec.coffee
+++ b/spec/selection-spec.coffee
@@ -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 ->
diff --git a/spec/spec-bootstrap.coffee b/spec/spec-bootstrap.coffee
deleted file mode 100644
index 5158c9bee..000000000
--- a/spec/spec-bootstrap.coffee
+++ /dev/null
@@ -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
diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee
index 085db7835..9a2ef83ce 100644
--- a/spec/spec-helper.coffee
+++ b/spec/spec-helper.coffee
@@ -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
diff --git a/spec/spec-suite.coffee b/spec/spec-suite.coffee
deleted file mode 100644
index 817de7986..000000000
--- a/spec/spec-suite.coffee
+++ /dev/null
@@ -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()
diff --git a/spec/style-manager-spec.coffee b/spec/style-manager-spec.coffee
index 7e601bcc1..d0a1cfe13 100644
--- a/spec/style-manager-spec.coffee
+++ b/spec/style-manager-spec.coffee
@@ -4,7 +4,7 @@ describe "StyleManager", ->
[manager, addEvents, removeEvents, updateEvents] = []
beforeEach ->
- manager = new StyleManager
+ manager = new StyleManager(configDirPath: atom.getConfigDirPath())
addEvents = []
removeEvents = []
updateEvents = []
diff --git a/spec/styles-element-spec.coffee b/spec/styles-element-spec.coffee
index 7d2fe722a..b1a57938c 100644
--- a/spec/styles-element-spec.coffee
+++ b/spec/styles-element-spec.coffee
@@ -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()
diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee
index 45e1dbb6b..b0f3ace51 100644
--- a/spec/text-editor-component-spec.coffee
+++ b/spec/text-editor-component-spec.coffee
@@ -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()
diff --git a/spec/text-editor-element-spec.coffee b/spec/text-editor-element-spec.coffee
index 148199425..e7d5831eb 100644
--- a/spec/text-editor-element-spec.coffee
+++ b/spec/text-editor-element-spec.coffee
@@ -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)
diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee
index acb9c29ae..1804b7b6b 100644
--- a/spec/text-editor-presenter-spec.coffee
+++ b/spec/text-editor-presenter-spec.coffee
@@ -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
diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee
index daba04714..dd01b816f 100644
--- a/spec/text-editor-spec.coffee
+++ b/spec/text-editor-spec.coffee
@@ -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()
diff --git a/spec/token-iterator-spec.coffee b/spec/token-iterator-spec.coffee
index ee8ac25e5..f876d30d1 100644
--- a/spec/token-iterator-spec.coffee
+++ b/spec/token-iterator-spec.coffee
@@ -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()
diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee
index 4945b37ec..e87125195 100644
--- a/spec/tokenized-buffer-spec.coffee
+++ b/spec/tokenized-buffer-spec.coffee
@@ -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 "
<%= User.find(2).full_name %>
"
- 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 ->
diff --git a/spec/tokenized-line-spec.coffee b/spec/tokenized-line-spec.coffee
index 2914ec089..f1dce7b9e 100644
--- a/spec/tokenized-line-spec.coffee
+++ b/spec/tokenized-line-spec.coffee
@@ -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
diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee
index ec30ef2e3..bf0462ebf 100644
--- a/spec/tooltip-manager-spec.coffee
+++ b/spec/tooltip-manager-spec.coffee
@@ -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)
diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee
index 345c0fd8c..3148942b4 100644
--- a/spec/window-event-handler-spec.coffee
+++ b/spec/window-event-handler-spec.coffee
@@ -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 = []
diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee
index c60508640..5850100e0 100644
--- a/spec/workspace-spec.coffee
+++ b/spec/workspace-spec.coffee
@@ -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')
diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee
new file mode 100644
index 000000000..f2b425137
--- /dev/null
+++ b/src/application-delegate.coffee
@@ -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)
diff --git a/src/atom.coffee b/src/atom-environment.coffee
similarity index 58%
rename from src/atom.coffee
rename to src/atom-environment.coffee
index 59d7765ac..f60f2ec97 100644
--- a/src/atom.coffee
+++ b/src/atom-environment.coffee
@@ -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) ->
diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee
index df4254acf..b8e3e7bc2 100644
--- a/src/browser/atom-application.coffee
+++ b/src/browser/atom-application.coffee
@@ -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
diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee
index e341f25d3..3be46b5fc 100644
--- a/src/browser/atom-window.coffee
+++ b/src/browser/atom-window.coffee
@@ -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'
diff --git a/src/browser/main.coffee b/src/browser/main.coffee
index f0709037d..6a141cf1a 100644
--- a/src/browser/main.coffee
+++ b/src/browser/main.coffee
@@ -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()
diff --git a/src/clipboard.coffee b/src/clipboard.coffee
index 84ff9ab3b..9511bdda9 100644
--- a/src/clipboard.coffee
+++ b/src/clipboard.coffee
@@ -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.
#
diff --git a/src/command-installer.coffee b/src/command-installer.coffee
index 729aef84c..3d366037c 100644
--- a/src/command-installer.coffee
+++ b/src/command-installer.coffee
@@ -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."
diff --git a/src/command-registry.coffee b/src/command-registry.coffee
index 4cf002c7e..db2cf498d 100644
--- a/src/command-registry.coffee
+++ b/src/command-registry.coffee
@@ -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
diff --git a/src/config.coffee b/src/config.coffee
index b0579716b..cb09a8d83 100644
--- a/src/config.coffee
+++ b/src/config.coffee
@@ -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
diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee
index 0ca005d8a..869076beb 100644
--- a/src/context-menu-manager.coffee
+++ b/src/context-menu-manager.coffee
@@ -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: ->
diff --git a/src/cursor.coffee b/src/cursor.coffee
index 84396448c..c9ebca8c7 100644
--- a/src/cursor.coffee
+++ b/src/cursor.coffee
@@ -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}]+"
diff --git a/src/custom-gutter-component.coffee b/src/custom-gutter-component.coffee
index 32710ea46..9fd25fd3f 100644
--- a/src/custom-gutter-component.coffee
+++ b/src/custom-gutter-component.coffee
@@ -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 = ''
diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee
index 62c7e4401..7f6cb0f65 100644
--- a/src/deserializer-manager.coffee
+++ b/src/deserializer-manager.coffee
@@ -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 = {}
diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee
index 2a93aa951..b3a5a628c 100644
--- a/src/display-buffer.coffee
+++ b/src/display-buffer.coffee
@@ -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}
diff --git a/src/git-repository-provider.coffee b/src/git-repository-provider.coffee
index 3b4df5d2b..593324d0c 100644
--- a/src/git-repository-provider.coffee
+++ b/src/git-repository-provider.coffee
@@ -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
diff --git a/src/git-repository.coffee b/src/git-repository.coffee
index 064b86dd3..1663f9ad4 100644
--- a/src/git-repository.coffee
+++ b/src/git-repository.coffee
@@ -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?
diff --git a/src/grammar-registry.coffee b/src/grammar-registry.coffee
index 109e719fc..4a5352275 100644
--- a/src/grammar-registry.coffee
+++ b/src/grammar-registry.coffee
@@ -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
diff --git a/src/gutter-container-component.coffee b/src/gutter-container-component.coffee
index 40e2c8c26..c5538324b 100644
--- a/src/gutter-container-component.coffee
+++ b/src/gutter-container-component.coffee
@@ -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.
diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee
new file mode 100644
index 000000000..acf50bc5d
--- /dev/null
+++ b/src/initialize-application-window.coffee
@@ -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)
diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee
new file mode 100644
index 000000000..ad15429ff
--- /dev/null
+++ b/src/initialize-test-window.coffee
@@ -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
diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee
index da77f0156..82f2e8b99 100644
--- a/src/keymap-extensions.coffee
+++ b/src/keymap-extensions.coffee
@@ -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
diff --git a/src/language-mode.coffee b/src/language-mode.coffee
index e60e85f7f..55707bad3 100644
--- a/src/language-mode.coffee
+++ b/src/language-mode.coffee
@@ -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
diff --git a/src/line-number-gutter-component.coffee b/src/line-number-gutter-component.coffee
index b4a5bc168..bb66ff144 100644
--- a/src/line-number-gutter-component.coffee
+++ b/src/line-number-gutter-component.coffee
@@ -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 = ''
diff --git a/src/lines-component.coffee b/src/lines-component.coffee
index 98b274fae..b5af56885 100644
--- a/src/lines-component.coffee
+++ b/src/lines-component.coffee
@@ -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: {}}
diff --git a/src/lines-tile-component.coffee b/src/lines-tile-component.coffee
index 619acc56a..6b4ac80ba 100644
--- a/src/lines-tile-component.coffee
+++ b/src/lines-tile-component.coffee
@@ -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 = {}
diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee
index 0dc3e7602..3254451f1 100644
--- a/src/lines-yardstick.coffee
+++ b/src/lines-yardstick.coffee
@@ -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()
diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee
index b138f2da2..fa78d3cd6 100644
--- a/src/menu-manager.coffee
+++ b/src/menu-manager.coffee
@@ -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'
diff --git a/src/overlay-manager.coffee b/src/overlay-manager.coffee
index 7858cb8a2..83787ad10 100644
--- a/src/overlay-manager.coffee
+++ b/src/overlay-manager.coffee
@@ -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')
diff --git a/src/package-manager.coffee b/src/package-manager.coffee
index 7e4a19470..f6cef9465 100644
--- a/src/package-manager.coffee
+++ b/src/package-manager.coffee
@@ -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\+/, '')
diff --git a/src/package.coffee b/src/package.coffee
index 0163f8bcd..00963ef28 100644
--- a/src/package.coffee
+++ b/src/package.coffee
@@ -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})
diff --git a/src/pane-axis-element.coffee b/src/pane-axis-element.coffee
index 91f27858e..eaa26a9fe 100644
--- a/src/pane-axis-element.coffee
+++ b/src/pane-axis-element.coffee
@@ -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)
diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee
index 57d07d8a9..d104e984b 100644
--- a/src/pane-axis.coffee
+++ b/src/pane-axis.coffee
@@ -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
diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee
index b3d4e0036..9da452558 100644
--- a/src/pane-container-element.coffee
+++ b/src/pane-container-element.coffee
@@ -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'))
diff --git a/src/pane-container.coffee b/src/pane-container.coffee
index a8d1fa5d2..8ad082a5f 100644
--- a/src/pane-container.coffee
+++ b/src/pane-container.coffee
@@ -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
diff --git a/src/pane-element.coffee b/src/pane-element.coffee
index c6ded79c4..59f003300 100644
--- a/src/pane-element.coffee
+++ b/src/pane-element.coffee
@@ -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
diff --git a/src/pane.coffee b/src/pane.coffee
index a146b66fc..5f0d8dc1b 100644
--- a/src/pane.coffee
+++ b/src/pane.coffee
@@ -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
diff --git a/src/panel-container-element.coffee b/src/panel-container-element.coffee
index e1459e6d6..40ce7e352 100644
--- a/src/panel-container-element.coffee
+++ b/src/panel-container-element.coffee
@@ -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()
diff --git a/src/panel-element.coffee b/src/panel-element.coffee
index 287d34940..8d6aadae4 100644
--- a/src/panel-element.coffee
+++ b/src/panel-element.coffee
@@ -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())
diff --git a/src/project.coffee b/src/project.coffee
index b3f05a942..935e3a213 100644
--- a/src/project.coffee
+++ b/src/project.coffee
@@ -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()}`.
""",
diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee
new file mode 100644
index 000000000..80b7ff6c6
--- /dev/null
+++ b/src/register-default-commands.coffee
@@ -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
diff --git a/src/scoped-properties.coffee b/src/scoped-properties.coffee
index 8bf4c5ed3..c7257083e 100644
--- a/src/scoped-properties.coffee
+++ b/src/scoped-properties.coffee
@@ -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
diff --git a/src/selection.coffee b/src/selection.coffee
index 80929cac7..2ba66ebb0 100644
--- a/src/selection.coffee
+++ b/src/selection.coffee
@@ -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
})
diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee
index bf969dee2..da8af3f2e 100644
--- a/src/storage-folder.coffee
+++ b/src/storage-folder.coffee
@@ -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')
diff --git a/src/style-manager.coffee b/src/style-manager.coffee
index cfe86b3fe..8f932d229 100644
--- a/src/style-manager.coffee
+++ b/src/style-manager.coffee
@@ -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')
diff --git a/src/styles-element.coffee b/src/styles-element.coffee
index fc3b888cf..d1e6bf3d9 100644
--- a/src/styles-element.coffee
+++ b/src/styles-element.coffee
@@ -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) ->
diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee
index 3dd9f317c..8a6d885c2 100644
--- a/src/text-editor-component.coffee
+++ b/src/text-editor-component.coffee
@@ -38,15 +38,15 @@ class TextEditorComponent
Object.defineProperty @prototype, "domNode",
get: -> @domNodeValue
set: (domNode) ->
- atom.assert domNode?, "TextEditorComponent::domNode was set to null."
+ @assert domNode?, "TextEditorComponent::domNode was set to null."
@domNodeValue = domNode
- constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize}) ->
+ constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars}) ->
@tileSize = tileSize if tileSize?
@disposables = new CompositeDisposable
@observeConfig()
- @setScrollSensitivity(atom.config.get('editor.scrollSensitivity'))
+ @setScrollSensitivity(@config.get('editor.scrollSensitivity'))
@presenter = new TextEditorPresenter
model: @editor
@@ -58,6 +58,7 @@ class TextEditorComponent
cursorBlinkPeriod: @cursorBlinkPeriod
cursorBlinkResumeDelay: @cursorBlinkResumeDelay
stoppedScrollingDelay: 200
+ config: @config
@presenter.onDidUpdateState(@requestUpdate)
@@ -70,10 +71,10 @@ class TextEditorComponent
insertionPoint = document.createElement('content')
insertionPoint.setAttribute('select', 'atom-overlay')
@domNode.appendChild(insertionPoint)
- @overlayManager = new OverlayManager(@presenter, @hostElement)
+ @overlayManager = new OverlayManager(@presenter, @hostElement, @views)
else
@domNode.classList.add('editor-contents')
- @overlayManager = new OverlayManager(@presenter, @domNode)
+ @overlayManager = new OverlayManager(@presenter, @domNode, @views)
@scrollViewNode = document.createElement('div')
@scrollViewNode.classList.add('scroll-view')
@@ -82,10 +83,10 @@ class TextEditorComponent
@hiddenInputComponent = new InputComponent
@scrollViewNode.appendChild(@hiddenInputComponent.getDomNode())
- @linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM, @domElementPool})
+ @linesComponent = new LinesComponent({@presenter, @hostElement, @useShadowDOM, @domElementPool, @assert, @grammars})
@scrollViewNode.appendChild(@linesComponent.getDomNode())
- @linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent)
+ @linesYardstick = new LinesYardstick(@editor, @presenter, @linesComponent, @grammars)
@presenter.setLinesYardstick(@linesYardstick)
@horizontalScrollbarComponent = new ScrollbarComponent({orientation: 'horizontal', onScroll: @onHorizontalScroll})
@@ -103,11 +104,11 @@ class TextEditorComponent
@disposables.add @stylesElement.onDidAddStyleElement @onStylesheetsChanged
@disposables.add @stylesElement.onDidUpdateStyleElement @onStylesheetsChanged
@disposables.add @stylesElement.onDidRemoveStyleElement @onStylesheetsChanged
- unless atom.themes.isInitialLoadComplete()
- @disposables.add atom.themes.onDidChangeActiveThemes @onAllThemesLoaded
+ unless @themes.isInitialLoadComplete()
+ @disposables.add @themes.onDidChangeActiveThemes @onAllThemesLoaded
@disposables.add scrollbarStyle.onDidChangePreferredScrollbarStyle @refreshScrollbars
- @disposables.add atom.views.pollDocument(@pollDOM)
+ @disposables.add @views.pollDocument(@pollDOM)
@updateSync()
@checkForVisibilityChange()
@@ -177,7 +178,7 @@ class TextEditorComponent
@overlayManager?.measureOverlays()
mountGutterContainerComponent: ->
- @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @domElementPool})
+ @gutterContainerComponent = new GutterContainerComponent({@editor, @onLineNumberGutterMouseDown, @domElementPool, @views})
@domNode.insertBefore(@gutterContainerComponent.getDomNode(), @domNode.firstChild)
becameVisible: ->
@@ -204,10 +205,10 @@ class TextEditorComponent
@updateSync()
else unless @updateRequested
@updateRequested = true
- atom.views.updateDocument =>
+ @views.updateDocument =>
@updateRequested = false
@updateSync() if @canUpdate()
- atom.views.readDocument(@readAfterUpdateSync)
+ @views.readDocument(@readAfterUpdateSync)
canUpdate: ->
@mounted and @editor.isAlive()
@@ -275,13 +276,13 @@ class TextEditorComponent
timeoutId = setTimeout(writeSelectedTextToSelectionClipboard)
observeConfig: ->
- @disposables.add atom.config.onDidChange 'editor.fontSize', =>
+ @disposables.add @config.onDidChange 'editor.fontSize', =>
@sampleFontStyling()
@invalidateCharacterWidths()
- @disposables.add atom.config.onDidChange 'editor.fontFamily', =>
+ @disposables.add @config.onDidChange 'editor.fontFamily', =>
@sampleFontStyling()
@invalidateCharacterWidths()
- @disposables.add atom.config.onDidChange 'editor.lineHeight', =>
+ @disposables.add @config.onDidChange 'editor.lineHeight', =>
@sampleFontStyling()
@invalidateCharacterWidths()
@@ -294,7 +295,7 @@ class TextEditorComponent
@disposables.add(@scopedConfigDisposables)
scope = @editor.getRootScopeDescriptor()
- @scopedConfigDisposables.add atom.config.observe 'editor.scrollSensitivity', {scope}, @setScrollSensitivity
+ @scopedConfigDisposables.add @config.observe 'editor.scrollSensitivity', {scope}, @setScrollSensitivity
focused: ->
if @mounted
@@ -354,11 +355,11 @@ class TextEditorComponent
{wheelDeltaX, wheelDeltaY} = event
# Ctrl+MouseWheel adjusts font size.
- if event.ctrlKey and atom.config.get('editor.zoomFontWhenCtrlScrolling')
+ if event.ctrlKey and @config.get('editor.zoomFontWhenCtrlScrolling')
if wheelDeltaY > 0
- atom.workspace.increaseFontSize()
+ @workspace.increaseFontSize()
else if wheelDeltaY < 0
- atom.workspace.decreaseFontSize()
+ @workspace.decreaseFontSize()
event.preventDefault()
return
@@ -543,7 +544,7 @@ class TextEditorComponent
onStylesheetsChanged: (styleElement) =>
return unless @performedInitialMeasurement
- return unless atom.themes.isInitialLoadComplete()
+ return unless @themes.isInitialLoadComplete()
# This delay prevents the styling from going haywire when stylesheets are
# reloaded in dev mode. It seems like a workaround for a browser bug, but
@@ -650,7 +651,7 @@ class TextEditorComponent
isVisible: ->
# Investigating an exception that occurs here due to ::domNode being null.
- atom.assert @domNode?, "TextEditorComponent::domNode was null.", (error) =>
+ @assert @domNode?, "TextEditorComponent::domNode was null.", (error) =>
error.metadata = {@initialized}
@domNode? and (@domNode.offsetHeight > 0 or @domNode.offsetWidth > 0)
@@ -848,7 +849,7 @@ class TextEditorComponent
@presenter.characterWidthsChanged()
setShowIndentGuide: (showIndentGuide) ->
- atom.config.set("editor.showIndentGuide", showIndentGuide)
+ @config.set("editor.showIndentGuide", showIndentGuide)
setScrollSensitivity: (scrollSensitivity) =>
if scrollSensitivity = parseInt(scrollSensitivity)
diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee
index 405cf30e4..9fe0e9091 100644
--- a/src/text-editor-element.coffee
+++ b/src/text-editor-element.coffee
@@ -4,6 +4,7 @@ Path = require 'path'
TextBuffer = require 'text-buffer'
TextEditor = require './text-editor'
TextEditorComponent = require './text-editor-component'
+StylesElement = require './styles-element'
ShadowStyleSheet = null
@@ -18,29 +19,38 @@ class TextEditorElement extends HTMLElement
logicalDisplayBuffer: true
createdCallback: ->
+ # Use globals when the following instance variables aren't set.
+ @config = atom.config
+ @themes = atom.themes
+ @workspace = atom.workspace
+ @assert = atom.assert
+ @views = atom.views
+ @styles = atom.styles
+ @grammars = atom.grammars
+
@emitter = new Emitter
@subscriptions = new CompositeDisposable
- @initializeContent()
+
@addEventListener 'focus', @focused.bind(this)
@addEventListener 'blur', @blurred.bind(this)
- initializeContent: (attributes) ->
@classList.add('editor')
@setAttribute('tabindex', -1)
- if atom.config.get('editor.useShadowDOM')
+ initializeContent: (attributes) ->
+ if @config.get('editor.useShadowDOM')
@useShadowDOM = true
unless ShadowStyleSheet?
ShadowStyleSheet = document.createElement('style')
- ShadowStyleSheet.textContent = atom.themes.loadLessStylesheet(require.resolve('../static/text-editor-shadow.less'))
+ ShadowStyleSheet.textContent = @themes.loadLessStylesheet(require.resolve('../static/text-editor-shadow.less'))
@createShadowRoot()
@shadowRoot.appendChild(ShadowStyleSheet.cloneNode(true))
- @stylesElement = document.createElement('atom-styles')
+ @stylesElement = new StylesElement
+ @stylesElement.initialize(@styles)
@stylesElement.setAttribute('context', 'atom-text-editor')
- @stylesElement.initialize()
@rootElement = document.createElement('div')
@rootElement.classList.add('editor--private')
@@ -56,7 +66,7 @@ class TextEditorElement extends HTMLElement
attachedCallback: ->
@buildModel() unless @getModel()?
- atom.assert(@model.isAlive(), "Attaching a view for a destroyed editor")
+ @assert(@model.isAlive(), "Attaching a view for a destroyed editor")
@mountComponent() unless @component?
@listenForComponentEvents()
@component.checkForVisibilityChange()
@@ -76,7 +86,15 @@ class TextEditorElement extends HTMLElement
@subscriptions.add @component.onDidChangeScrollLeft =>
@emitter.emit("did-change-scroll-left", arguments...)
- initialize: (model) ->
+ initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}) ->
+ throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views?
+ throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config?
+ throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes?
+ throw new Error("Must pass a workspace parameter when initializing TextEditorElements") unless @workspace?
+ throw new Error("Must pass a assert parameter when initializing TextEditorElements") unless @assert?
+ throw new Error("Must pass a styles parameter when initializing TextEditorElements") unless @styles?
+ throw new Error("Must pass a grammars parameter when initializing TextEditorElements") unless @grammars?
+
@setModel(model)
this
@@ -85,6 +103,7 @@ class TextEditorElement extends HTMLElement
return if model.isDestroyed()
@model = model
+ @initializeContent()
@mountComponent()
@addGrammarScopeAttribute()
@addMiniAttribute() if @model.isMini()
@@ -99,7 +118,7 @@ class TextEditorElement extends HTMLElement
@model ? @buildModel()
buildModel: ->
- @setModel(new TextEditor(
+ @setModel(@workspace.buildTextEditor(
buffer: new TextBuffer(@textContent)
softWrapped: false
tabLength: 2
@@ -117,6 +136,12 @@ class TextEditorElement extends HTMLElement
editor: @model
tileSize: @tileSize
useShadowDOM: @useShadowDOM
+ views: @views
+ themes: @themes
+ config: @config
+ workspace: @workspace
+ assert: @assert
+ grammars: @grammars
)
@rootElement.appendChild(@component.getDomNode())
@@ -313,141 +338,4 @@ class TextEditorElement extends HTMLElement
getHeight: ->
@offsetHeight
-stopEventPropagation = (commandListeners) ->
- newCommandListeners = {}
- for commandName, commandListener of commandListeners
- do (commandListener) ->
- newCommandListeners[commandName] = (event) ->
- event.stopPropagation()
- commandListener.call(@getModel(), event)
- newCommandListeners
-
-stopEventPropagationAndGroupUndo = (commandListeners) ->
- newCommandListeners = {}
- for commandName, commandListener of commandListeners
- do (commandListener) ->
- newCommandListeners[commandName] = (event) ->
- event.stopPropagation()
- model = @getModel()
- model.transact atom.config.get('editor.undoGroupingInterval'), ->
- commandListener.call(model, event)
- newCommandListeners
-
-atom.commands.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()
-)
-
-atom.commands.add 'atom-text-editor', stopEventPropagationAndGroupUndo(
- '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()
-)
-
-atom.commands.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': -> atom.config.set('editor.showIndentGuide', not atom.config.get('editor.showIndentGuide'))
- 'editor:toggle-line-numbers': -> atom.config.set('editor.showLineNumbers', not atom.config.get('editor.showLineNumbers'))
- 'editor:scroll-to-cursor': -> @scrollToCursorPosition()
-)
-
-atom.commands.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(
- '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()
-)
-
module.exports = TextEditorElement = document.registerElement 'atom-text-editor', prototype: TextEditorElement.prototype
diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee
index 985ca1c95..f416f171c 100644
--- a/src/text-editor-presenter.coffee
+++ b/src/text-editor-presenter.coffee
@@ -13,7 +13,7 @@ class TextEditorPresenter
minimumReflowInterval: 200
constructor: (params) ->
- {@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @scrollColumn, @scrollRow, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params
+ {@model, @config, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @scrollColumn, @scrollRow, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params
{horizontalScrollbarHeight, verticalScrollbarWidth} = params
{@lineHeight, @baseCharacterWidth, @backgroundColor, @gutterBackgroundColor, @tileSize} = params
{@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @focused} = params
@@ -225,9 +225,9 @@ class TextEditorPresenter
observeConfig: ->
configParams = {scope: @model.getRootScopeDescriptor()}
- @scrollPastEnd = atom.config.get('editor.scrollPastEnd', configParams)
- @showLineNumbers = atom.config.get('editor.showLineNumbers', configParams)
- @showIndentGuide = atom.config.get('editor.showIndentGuide', configParams)
+ @scrollPastEnd = @config.get('editor.scrollPastEnd', configParams)
+ @showLineNumbers = @config.get('editor.showLineNumbers', configParams)
+ @showIndentGuide = @config.get('editor.showIndentGuide', configParams)
if @configDisposables?
@configDisposables?.dispose()
@@ -236,19 +236,19 @@ class TextEditorPresenter
@configDisposables = new CompositeDisposable
@disposables.add(@configDisposables)
- @configDisposables.add atom.config.onDidChange 'editor.showIndentGuide', configParams, ({newValue}) =>
+ @configDisposables.add @config.onDidChange 'editor.showIndentGuide', configParams, ({newValue}) =>
@showIndentGuide = newValue
@shouldUpdateContentState = true
@emitDidUpdateState()
- @configDisposables.add atom.config.onDidChange 'editor.scrollPastEnd', configParams, ({newValue}) =>
+ @configDisposables.add @config.onDidChange 'editor.scrollPastEnd', configParams, ({newValue}) =>
@scrollPastEnd = newValue
@shouldUpdateVerticalScrollState = true
@shouldUpdateScrollbarsState = true
@updateScrollHeight()
@emitDidUpdateState()
- @configDisposables.add atom.config.onDidChange 'editor.showLineNumbers', configParams, ({newValue}) =>
+ @configDisposables.add @config.onDidChange 'editor.showLineNumbers', configParams, ({newValue}) =>
@showLineNumbers = newValue
@shouldUpdateLineNumberGutterState = true
@shouldUpdateGutterOrderState = true
diff --git a/src/text-editor.coffee b/src/text-editor.coffee
index 2610c5970..4c6a0f514 100644
--- a/src/text-editor.coffee
+++ b/src/text-editor.coffee
@@ -54,10 +54,7 @@ GutterContainer = require './gutter-container'
# soft wraps and folds to ensure your code interacts with them correctly.
module.exports =
class TextEditor extends Model
- atom.deserializers.add(this)
-
callDisplayBufferCreatedHook: false
- registerEditor: false
buffer: null
languageMode: null
cursors: null
@@ -67,9 +64,9 @@ class TextEditor extends Model
selectionFlashDuration: 500
gutterContainer: null
- @deserialize: (state) ->
+ @deserialize: (state, atomEnvironment) ->
try
- displayBuffer = DisplayBuffer.deserialize(state.displayBuffer)
+ displayBuffer = DisplayBuffer.deserialize(state.displayBuffer, atomEnvironment)
catch error
if error.syscall is 'read'
return # Error reading the file, don't deserialize an editor for it
@@ -77,19 +74,46 @@ class TextEditor extends Model
throw error
state.displayBuffer = displayBuffer
- state.registerEditor = true
+ state.config = atomEnvironment.config
+ state.notificationManager = atomEnvironment.notifications
+ state.packageManager = atomEnvironment.packages
+ state.clipboard = atomEnvironment.clipboard
+ state.viewRegistry = atomEnvironment.views
+ state.grammarRegistry = atomEnvironment.grammars
+ state.project = atomEnvironment.project
+ state.assert = atomEnvironment.assert.bind(atomEnvironment)
+ state.applicationDelegate = atomEnvironment.applicationDelegate
new this(state)
- constructor: ({@softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode}={}) ->
+ constructor: (params={}) ->
super
+ {
+ @softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength,
+ softWrapped, @displayBuffer, buffer, suppressCursorCreation, @mini, @placeholderText,
+ lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager,
+ @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate
+ } = params
+
+ throw new Error("Must pass a config parameter when constructing TextEditors") unless @config?
+ throw new Error("Must pass a notificationManager parameter when constructing TextEditors") unless @notificationManager?
+ throw new Error("Must pass a packageManager parameter when constructing TextEditors") unless @packageManager?
+ throw new Error("Must pass a clipboard parameter when constructing TextEditors") unless @clipboard?
+ throw new Error("Must pass a viewRegistry parameter when constructing TextEditors") unless @viewRegistry?
+ throw new Error("Must pass a grammarRegistry parameter when constructing TextEditors") unless @grammarRegistry?
+ throw new Error("Must pass a project parameter when constructing TextEditors") unless @project?
+ throw new Error("Must pass an assert parameter when constructing TextEditors") unless @assert?
+
@emitter = new Emitter
@disposables = new CompositeDisposable
@cursors = []
@selections = []
buffer ?= new TextBuffer
- @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode})
+ @displayBuffer ?= new DisplayBuffer({
+ buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode,
+ @config, @assert, @grammarRegistry, @packageManager
+ })
@buffer = @displayBuffer.buffer
for marker in @findMarkers(@getSelectionMarkerAttributes())
@@ -105,9 +129,9 @@ class TextEditor extends Model
initialColumn = Math.max(parseInt(initialColumn) or 0, 0)
@addCursorAtBufferPosition([initialLine, initialColumn])
- @languageMode = new LanguageMode(this)
+ @languageMode = new LanguageMode(this, @config)
- @setEncoding(atom.config.get('core.fileEncoding', scope: @getRootScopeDescriptor()))
+ @setEncoding(@config.get('core.fileEncoding', scope: @getRootScopeDescriptor()))
@gutterContainer = new GutterContainer(this)
@lineNumberGutter = @gutterContainer.addGutter
@@ -115,8 +139,6 @@ class TextEditor extends Model
priority: 0
visible: lineNumberGutterVisible
- atom.workspace?.editorAdded(this) if registerEditor
-
serialize: ->
deserializer: 'TextEditor'
id: @id
@@ -128,8 +150,8 @@ class TextEditor extends Model
subscribeToBuffer: ->
@buffer.retain()
@disposables.add @buffer.onDidChangePath =>
- unless atom.project.getPaths().length > 0
- atom.project.setPaths([path.dirname(@getPath())])
+ unless @project.getPaths().length > 0
+ @project.setPaths([path.dirname(@getPath())])
@emitter.emit 'did-change-title', @getTitle()
@emitter.emit 'did-change-path', @getPath()
@disposables.add @buffer.onDidChangeEncoding =>
@@ -148,7 +170,7 @@ class TextEditor extends Model
subscribeToTabTypeConfig: ->
@tabTypeSubscription?.dispose()
- @tabTypeSubscription = atom.config.observe 'editor.tabType', scope: @getRootScopeDescriptor(), =>
+ @tabTypeSubscription = @config.observe 'editor.tabType', scope: @getRootScopeDescriptor(), =>
@softTabs = @shouldUseSoftTabs(defaultValue: @softTabs)
destroyed: ->
@@ -429,12 +451,12 @@ class TextEditor extends Model
onDidChangeScrollTop: (callback) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.")
- atom.views.getView(this).onDidChangeScrollTop(callback)
+ @viewRegistry.getView(this).onDidChangeScrollTop(callback)
onDidChangeScrollLeft: (callback) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollLeft instead.")
- atom.views.getView(this).onDidChangeScrollLeft(callback)
+ @viewRegistry.getView(this).onDidChangeScrollLeft(callback)
onDidRequestAutoscroll: (callback) ->
@displayBuffer.onDidRequestAutoscroll(callback)
@@ -456,7 +478,11 @@ class TextEditor extends Model
copy: ->
displayBuffer = @displayBuffer.copy()
softTabs = @getSoftTabs()
- newEditor = new TextEditor({@buffer, displayBuffer, @tabLength, softTabs, suppressCursorCreation: true, registerEditor: true})
+ newEditor = new TextEditor({
+ @buffer, displayBuffer, @tabLength, softTabs, suppressCursorCreation: true,
+ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry,
+ @grammarRegistry, @project, @assert, @applicationDelegate
+ })
for marker in @findMarkers(editorId: @id)
marker.copy(editorId: newEditor.id, preserveFolds: true)
newEditor
@@ -557,7 +583,7 @@ class TextEditor extends Model
getLongTitle: ->
if sessionPath = @getPath()
fileName = path.basename(sessionPath)
- directory = atom.project.relativize(path.dirname(sessionPath))
+ directory = @project.relativize(path.dirname(sessionPath))
directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
"#{fileName} - #{directory}"
else
@@ -585,7 +611,7 @@ class TextEditor extends Model
# Copies the current file path to the native clipboard.
copyPathToClipboard: ->
if filePath = @getPath()
- atom.clipboard.write(filePath)
+ @clipboard.write(filePath)
###
Section: File Operations
@@ -594,14 +620,14 @@ class TextEditor extends Model
# Essential: Saves the editor's text buffer.
#
# See {TextBuffer::save} for more details.
- save: -> @buffer.save(backup: atom.config.get('editor.backUpBeforeSaving'))
+ save: -> @buffer.save(backup: @config.get('editor.backUpBeforeSaving'))
# Essential: Saves the editor's text buffer as the given path.
#
# See {TextBuffer::saveAs} for more details.
#
# * `filePath` A {String} path.
- saveAs: (filePath) -> @buffer.saveAs(filePath, backup: atom.config.get('editor.backUpBeforeSaving'))
+ saveAs: (filePath) -> @buffer.saveAs(filePath, backup: @config.get('editor.backUpBeforeSaving'))
# Determine whether the user should be prompted to save before closing
# this editor.
@@ -617,9 +643,20 @@ class TextEditor extends Model
checkoutHeadRevision: ->
if filePath = this.getPath()
- atom.project.repositoryForDirectory(new Directory(path.dirname(filePath)))
- .then (repository) =>
- repository?.checkoutHeadForEditor(this)
+ checkoutHead = =>
+ @project.repositoryForDirectory(new Directory(path.dirname(filePath)))
+ .then (repository) =>
+ repository?.checkoutHeadForEditor(this)
+
+ if @config.get('editor.confirmCheckoutHeadRevision')
+ @applicationDelegate.confirm
+ message: 'Confirm Checkout HEAD Revision'
+ detailedMessage: "Are you sure you want to discard all changes to \"#{path.basename(filePath)}\" since the last Git commit?"
+ buttons:
+ OK: checkoutHead
+ Cancel: null
+ else
+ checkoutHead()
else
Promise.resolve(false)
@@ -748,7 +785,7 @@ class TextEditor extends Model
return false unless @emitWillInsertTextEvent(text)
groupingInterval = if options.groupUndo
- atom.config.get('editor.undoGroupingInterval')
+ @config.get('editor.undoGroupingInterval')
else
0
@@ -1742,7 +1779,7 @@ class TextEditor extends Model
# Add a cursor based on the given {Marker}.
addCursor: (marker) ->
- cursor = new Cursor(editor: this, marker: marker)
+ cursor = new Cursor(editor: this, marker: marker, config: @config)
@cursors.push(cursor)
@decorateMarker(marker, type: 'line-number', class: 'cursor-line')
@decorateMarker(marker, type: 'line-number', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true)
@@ -2225,7 +2262,7 @@ class TextEditor extends Model
unless marker.getProperties().preserveFolds
@destroyFoldsContainingBufferRange(marker.getBufferRange())
cursor = @addCursor(marker)
- selection = new Selection(_.extend({editor: this, marker, cursor}, options))
+ selection = new Selection(_.extend({editor: this, marker, cursor, @clipboard}, options))
@selections.push(selection)
selectionBufferRange = selection.getBufferRange()
@mergeIntersectingSelections(preserveFolds: marker.getProperties().preserveFolds)
@@ -2382,10 +2419,10 @@ class TextEditor extends Model
#
# Returns a {Boolean}
shouldUseSoftTabs: ({defaultValue}) ->
- tabType = atom.config.get('editor.tabType', scope: @getRootScopeDescriptor())
+ tabType = @config.get('editor.tabType', scope: @getRootScopeDescriptor())
switch tabType
when 'auto'
- @usesSoftTabs() ? defaultValue ? atom.config.get('editor.softTabs') ? true
+ @usesSoftTabs() ? defaultValue ? @config.get('editor.softTabs') ? true
when 'hard'
false
when 'soft'
@@ -2561,7 +2598,7 @@ class TextEditor extends Model
list = list.map (item) -> "* #{item}"
content = "Scopes at Cursor\n#{list.join('\n')}"
- atom.notifications.addInfo(content, dismissable: true)
+ @notificationManager.addInfo(content, dismissable: true)
# {Delegates to: DisplayBuffer.tokenForBufferPosition}
tokenForBufferPosition: (bufferPosition) -> @displayBuffer.tokenForBufferPosition(bufferPosition)
@@ -2613,7 +2650,7 @@ class TextEditor extends Model
#
# * `options` (optional) See {Selection::insertText}.
pasteText: (options={}) ->
- {text: clipboardText, metadata} = atom.clipboard.readWithMetadata()
+ {text: clipboardText, metadata} = @clipboard.readWithMetadata()
return false unless @emitWillInsertTextEvent(clipboardText)
metadata ?= {}
@@ -2866,24 +2903,24 @@ class TextEditor extends Model
scrollToTop: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.")
- atom.views.getView(this).scrollToTop()
+ @viewRegistry.getView(this).scrollToTop()
scrollToBottom: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.")
- atom.views.getView(this).scrollToBottom()
+ @viewRegistry.getView(this).scrollToBottom()
scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options)
getHorizontalScrollbarHeight: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getHorizontalScrollbarHeight instead.")
- atom.views.getView(this).getHorizontalScrollbarHeight()
+ @viewRegistry.getView(this).getHorizontalScrollbarHeight()
getVerticalScrollbarWidth: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getVerticalScrollbarWidth instead.")
- atom.views.getView(this).getVerticalScrollbarWidth()
+ @viewRegistry.getView(this).getVerticalScrollbarWidth()
pageUp: ->
@moveUp(@getRowsPerPage())
@@ -2908,10 +2945,10 @@ class TextEditor extends Model
###
shouldAutoIndent: ->
- atom.config.get("editor.autoIndent", scope: @getRootScopeDescriptor())
+ @config.get("editor.autoIndent", scope: @getRootScopeDescriptor())
shouldAutoIndentOnPaste: ->
- atom.config.get("editor.autoIndentOnPaste", scope: @getRootScopeDescriptor())
+ @config.get("editor.autoIndentOnPaste", scope: @getRootScopeDescriptor())
###
Section: Event Handlers
@@ -2950,19 +2987,19 @@ class TextEditor extends Model
getFirstVisibleScreenRow: ->
deprecate("This is now a view method. Call TextEditorElement::getFirstVisibleScreenRow instead.")
- atom.views.getView(this).getVisibleRowRange()[0]
+ @viewRegistry.getView(this).getVisibleRowRange()[0]
getLastVisibleScreenRow: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getLastVisibleScreenRow instead.")
- atom.views.getView(this).getVisibleRowRange()[1]
+ @viewRegistry.getView(this).getVisibleRowRange()[1]
pixelPositionForBufferPosition: (bufferPosition) ->
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead")
- atom.views.getView(this).pixelPositionForBufferPosition(bufferPosition)
+ @viewRegistry.getView(this).pixelPositionForBufferPosition(bufferPosition)
pixelPositionForScreenPosition: (screenPosition) ->
Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead")
- atom.views.getView(this).pixelPositionForScreenPosition(screenPosition)
+ @viewRegistry.getView(this).pixelPositionForScreenPosition(screenPosition)
getSelectionMarkerAttributes: ->
{type: 'selection', editorId: @id, invalidate: 'never', maintainHistory: true}
@@ -2991,7 +3028,7 @@ class TextEditor extends Model
@displayBuffer.setHeight(height)
else
Grim.deprecate("This is now a view method. Call TextEditorElement::setHeight instead.")
- atom.views.getView(this).setHeight(height)
+ @viewRegistry.getView(this).setHeight(height)
getHeight: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getHeight instead.")
@@ -3004,7 +3041,7 @@ class TextEditor extends Model
@displayBuffer.setWidth(width)
else
Grim.deprecate("This is now a view method. Call TextEditorElement::setWidth instead.")
- atom.views.getView(this).setWidth(width)
+ @viewRegistry.getView(this).setWidth(width)
getWidth: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.")
@@ -3019,77 +3056,77 @@ class TextEditor extends Model
getScrollTop: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.")
- atom.views.getView(this).getScrollTop()
+ @viewRegistry.getView(this).getScrollTop()
setScrollTop: (scrollTop) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollTop instead.")
- atom.views.getView(this).setScrollTop(scrollTop)
+ @viewRegistry.getView(this).setScrollTop(scrollTop)
getScrollBottom: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollBottom instead.")
- atom.views.getView(this).getScrollBottom()
+ @viewRegistry.getView(this).getScrollBottom()
setScrollBottom: (scrollBottom) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollBottom instead.")
- atom.views.getView(this).setScrollBottom(scrollBottom)
+ @viewRegistry.getView(this).setScrollBottom(scrollBottom)
getScrollLeft: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollLeft instead.")
- atom.views.getView(this).getScrollLeft()
+ @viewRegistry.getView(this).getScrollLeft()
setScrollLeft: (scrollLeft) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollLeft instead.")
- atom.views.getView(this).setScrollLeft(scrollLeft)
+ @viewRegistry.getView(this).setScrollLeft(scrollLeft)
getScrollRight: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollRight instead.")
- atom.views.getView(this).getScrollRight()
+ @viewRegistry.getView(this).getScrollRight()
setScrollRight: (scrollRight) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollRight instead.")
- atom.views.getView(this).setScrollRight(scrollRight)
+ @viewRegistry.getView(this).setScrollRight(scrollRight)
getScrollHeight: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollHeight instead.")
- atom.views.getView(this).getScrollHeight()
+ @viewRegistry.getView(this).getScrollHeight()
getScrollWidth: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollWidth instead.")
- atom.views.getView(this).getScrollWidth()
+ @viewRegistry.getView(this).getScrollWidth()
getVisibleRowRange: ->
Grim.deprecate("This is now a view method. Call TextEditorElement::getVisibleRowRange instead.")
- atom.views.getView(this).getVisibleRowRange()
+ @viewRegistry.getView(this).getVisibleRowRange()
intersectsVisibleRowRange: (startRow, endRow) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.")
- atom.views.getView(this).intersectsVisibleRowRange(startRow, endRow)
+ @viewRegistry.getView(this).intersectsVisibleRowRange(startRow, endRow)
selectionIntersectsVisibleRowRange: (selection) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::selectionIntersectsVisibleRowRange instead.")
- atom.views.getView(this).selectionIntersectsVisibleRowRange(selection)
+ @viewRegistry.getView(this).selectionIntersectsVisibleRowRange(selection)
screenPositionForPixelPosition: (pixelPosition) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::screenPositionForPixelPosition instead.")
- atom.views.getView(this).screenPositionForPixelPosition(pixelPosition)
+ @viewRegistry.getView(this).screenPositionForPixelPosition(pixelPosition)
pixelRectForScreenRange: (screenRange) ->
Grim.deprecate("This is now a view method. Call TextEditorElement::pixelRectForScreenRange instead.")
- atom.views.getView(this).pixelRectForScreenRange(screenRange)
+ @viewRegistry.getView(this).pixelRectForScreenRange(screenRange)
###
Section: Utility
diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee
index 0d329cea7..649478841 100644
--- a/src/theme-manager.coffee
+++ b/src/theme-manager.coffee
@@ -9,12 +9,14 @@ fs = require 'fs-plus'
# An instance of this class is always available as the `atom.themes` global.
module.exports =
class ThemeManager
- constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode}) ->
+ constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode, @config, @styleManager, @notificationManager, @viewRegistry}) ->
@emitter = new Emitter
@styleSheetDisposablesBySourcePath = {}
@lessCache = null
@initialLoadComplete = false
@packageManager.registerPackageActivator(this, ['theme'])
+ @packageManager.onDidActivateInitialPackages =>
+ @onDidChangeActiveThemes => @packageManager.reloadActivePackageStyleSheets()
###
Section: Event Subscription
@@ -66,21 +68,21 @@ class ThemeManager
###
warnForNonExistentThemes: ->
- themeNames = atom.config.get('core.themes') ? []
+ themeNames = @config.get('core.themes') ? []
themeNames = [themeNames] unless _.isArray(themeNames)
for themeName in themeNames
- unless themeName and typeof themeName is 'string' and atom.packages.resolvePackagePath(themeName)
+ unless themeName and typeof themeName is 'string' and @packageManager.resolvePackagePath(themeName)
console.warn("Enabled theme '#{themeName}' is not installed.")
# Public: Get the enabled theme names from the config.
#
# Returns an array of theme names in the order that they should be activated.
getEnabledThemeNames: ->
- themeNames = atom.config.get('core.themes') ? []
+ themeNames = @config.get('core.themes') ? []
themeNames = [themeNames] unless _.isArray(themeNames)
- themeNames = themeNames.filter (themeName) ->
+ themeNames = themeNames.filter (themeName) =>
if themeName and typeof themeName is 'string'
- return true if atom.packages.resolvePackagePath(themeName)
+ return true if @packageManager.resolvePackagePath(themeName)
false
# Use a built-in syntax and UI theme any time the configured themes are not
@@ -139,7 +141,7 @@ class ThemeManager
loadUserStylesheet: ->
@unwatchUserStylesheet()
- userStylesheetPath = atom.styles.getUserStyleSheetPath()
+ userStylesheetPath = @styleManager.getUserStyleSheetPath()
return unless fs.isFileSync(userStylesheetPath)
try
@@ -158,14 +160,14 @@ class ThemeManager
[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)
try
userStylesheetContents = @loadStylesheet(userStylesheetPath, true)
catch
return
- @userStyleSheetDisposable = atom.styles.addStyleSheet(userStylesheetContents, sourcePath: userStylesheetPath, priority: 2)
+ @userStyleSheetDisposable = @styleManager.addStyleSheet(userStylesheetContents, sourcePath: userStylesheetPath, priority: 2)
loadBaseStylesheets: ->
@requireStylesheet('../static/bootstrap')
@@ -221,22 +223,22 @@ class ThemeManager
message = "Error loading Less stylesheet: `#{lessStylesheetPath}`"
detail = error.message
- atom.notifications.addError(message, {detail, dismissable: true})
+ @notificationManager.addError(message, {detail, dismissable: true})
throw error
removeStylesheet: (stylesheetPath) ->
@styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose()
applyStylesheet: (path, text) ->
- @styleSheetDisposablesBySourcePath[path] = atom.styles.addStyleSheet(text, sourcePath: path)
+ @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(text, sourcePath: path)
stringToId: (string) ->
string.replace(/\\/g, '/')
activateThemes: ->
new Promise (resolve) =>
- # atom.config.observe runs the callback once, then on subsequent changes.
- atom.config.observe 'core.themes', =>
+ # @config.observe runs the callback once, then on subsequent changes.
+ @config.observe 'core.themes', =>
@deactivateThemes()
@warnForNonExistentThemes()
@@ -268,13 +270,13 @@ class ThemeManager
isInitialLoadComplete: -> @initialLoadComplete
addActiveThemeClasses: ->
- if workspaceElement = atom.views.getView(atom.workspace)
+ if workspaceElement = @viewRegistry.getView(@workspace)
for pack in @getActiveThemes()
workspaceElement.classList.add("theme-#{pack.name}")
return
removeActiveThemeClasses: ->
- workspaceElement = atom.views.getView(atom.workspace)
+ workspaceElement = @viewRegistry.getView(@workspace)
for pack in @getActiveThemes()
workspaceElement.classList.remove("theme-#{pack.name}")
return
diff --git a/src/theme-package.coffee b/src/theme-package.coffee
index 6d0a88052..084728869 100644
--- a/src/theme-package.coffee
+++ b/src/theme-package.coffee
@@ -7,10 +7,10 @@ class ThemePackage extends Package
getStyleSheetPriority: -> 1
enable: ->
- atom.config.unshiftAtKeyPath('core.themes', @name)
+ @config.unshiftAtKeyPath('core.themes', @name)
disable: ->
- atom.config.removeAtKeyPath('core.themes', @name)
+ @config.removeAtKeyPath('core.themes', @name)
load: ->
@loadTime = 0
diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee
index 120f62fe4..92529d0e9 100644
--- a/src/token-iterator.coffee
+++ b/src/token-iterator.coffee
@@ -3,7 +3,7 @@
module.exports =
class TokenIterator
- constructor: (line) ->
+ constructor: ({@grammarRegistry}, line) ->
@reset(line) if line?
reset: (@line) ->
@@ -12,7 +12,7 @@ class TokenIterator
@bufferEnd = @bufferStart
@screenStart = 0
@screenEnd = 0
- @scopes = @line.openScopes.map (id) -> atom.grammars.scopeForId(id)
+ @scopes = @line.openScopes.map (id) => @grammarRegistry.scopeForId(id)
@scopeStarts = @scopes.slice()
@scopeEnds = []
this
@@ -32,7 +32,7 @@ class TokenIterator
while @index < tags.length
tag = tags[@index]
if tag < 0
- scope = atom.grammars.scopeForId(tag)
+ scope = @grammarRegistry.scopeForId(tag)
if tag % 2 is 0
if @scopeStarts[@scopeStarts.length - 1] is scope
@scopeStarts.pop()
diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee
index 8721b0547..2df29a31c 100644
--- a/src/tokenized-buffer.coffee
+++ b/src/tokenized-buffer.coffee
@@ -21,17 +21,26 @@ class TokenizedBuffer extends Model
configSettings: null
changeCount: 0
- @deserialize: (state) ->
- state.buffer = atom.project.bufferForPathSync(state.bufferPath)
+ @deserialize: (state, atomEnvironment) ->
+ state.buffer = atomEnvironment.project.bufferForPathSync(state.bufferPath)
+ state.config = atomEnvironment.config
+ state.grammarRegistry = atomEnvironment.grammars
+ state.packageManager = atomEnvironment.packages
+ state.assert = atomEnvironment.assert
new this(state)
- constructor: ({@buffer, @tabLength, @ignoreInvisibles, @largeFileMode}) ->
+ constructor: (params) ->
+ {
+ @buffer, @tabLength, @ignoreInvisibles, @largeFileMode, @config,
+ @grammarRegistry, @packageManager, @assert
+ } = params
+
@emitter = new Emitter
@disposables = new CompositeDisposable
- @tokenIterator = new TokenIterator
+ @tokenIterator = new TokenIterator({@grammarRegistry})
- @disposables.add atom.grammars.onDidAddGrammar(@grammarAddedOrUpdated)
- @disposables.add atom.grammars.onDidUpdateGrammar(@grammarAddedOrUpdated)
+ @disposables.add @grammarRegistry.onDidAddGrammar(@grammarAddedOrUpdated)
+ @disposables.add @grammarRegistry.onDidUpdateGrammar(@grammarAddedOrUpdated)
@disposables.add @buffer.preemptDidChange (e) => @handleBufferChange(e)
@disposables.add @buffer.onDidChangePath (@bufferPath) => @reloadGrammar()
@@ -65,7 +74,7 @@ class TokenizedBuffer extends Model
if grammar.injectionSelector?
@retokenizeLines() if @hasTokenForSelector(grammar.injectionSelector)
else
- newScore = atom.grammars.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
+ newScore = @grammarRegistry.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
setGrammar: (grammar, score) ->
@@ -73,7 +82,7 @@ class TokenizedBuffer extends Model
@grammar = grammar
@rootScopeDescriptor = new ScopeDescriptor(scopes: [@grammar.scopeName])
- @currentGrammarScore = score ? atom.grammars.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
+ @currentGrammarScore = score ? @grammarRegistry.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
@grammarUpdateDisposable?.dispose()
@grammarUpdateDisposable = @grammar.onDidUpdate => @retokenizeLines()
@@ -81,33 +90,33 @@ class TokenizedBuffer extends Model
scopeOptions = {scope: @rootScopeDescriptor}
@configSettings =
- tabLength: atom.config.get('editor.tabLength', scopeOptions)
- invisibles: atom.config.get('editor.invisibles', scopeOptions)
- showInvisibles: atom.config.get('editor.showInvisibles', scopeOptions)
+ tabLength: @config.get('editor.tabLength', scopeOptions)
+ invisibles: @config.get('editor.invisibles', scopeOptions)
+ showInvisibles: @config.get('editor.showInvisibles', scopeOptions)
if @configSubscriptions?
@configSubscriptions.dispose()
@disposables.remove(@configSubscriptions)
@configSubscriptions = new CompositeDisposable
- @configSubscriptions.add atom.config.onDidChange 'editor.tabLength', scopeOptions, ({newValue}) =>
+ @configSubscriptions.add @config.onDidChange 'editor.tabLength', scopeOptions, ({newValue}) =>
@configSettings.tabLength = newValue
@retokenizeLines()
['invisibles', 'showInvisibles'].forEach (key) =>
- @configSubscriptions.add atom.config.onDidChange "editor.#{key}", scopeOptions, ({newValue}) =>
+ @configSubscriptions.add @config.onDidChange "editor.#{key}", scopeOptions, ({newValue}) =>
oldInvisibles = @getInvisiblesToShow()
@configSettings[key] = newValue
@retokenizeLines() unless _.isEqual(@getInvisiblesToShow(), oldInvisibles)
@disposables.add(@configSubscriptions)
@retokenizeLines()
- atom.packages.triggerActivationHook("#{grammar.packageName}:grammar-used")
+ @packageManager.triggerActivationHook("#{grammar.packageName}:grammar-used")
@emitter.emit 'did-change-grammar', grammar
getGrammarSelectionContent: ->
@buffer.getTextInRange([[0, 0], [10, 0]])
reloadGrammar: ->
- if grammar = atom.grammars.selectGrammar(@buffer.getPath(), @getGrammarSelectionContent())
+ if grammar = @grammarRegistry.selectGrammar(@buffer.getPath(), @getGrammarSelectionContent())
@setGrammar(grammar)
else
throw new Error("No grammar found for path: #{path}")
@@ -155,7 +164,7 @@ class TokenizedBuffer extends Model
tokenizeNextChunk: ->
# Short circuit null grammar which can just use the placeholder tokens
- if @grammar is atom.grammars.nullGrammar and @firstInvalidRow()?
+ if @grammar is @grammarRegistry.nullGrammar and @firstInvalidRow()?
@invalidRows = []
@markTokenizationComplete()
return
@@ -291,7 +300,7 @@ class TokenizedBuffer extends Model
# undefined. This should paper over the problem but we want to figure out
# what is happening:
tokenizedLine = @tokenizedLineForRow(row)
- atom.assert tokenizedLine?, "TokenizedLine is undefined", (error) =>
+ @assert tokenizedLine?, "TokenizedLine is undefined", (error) =>
error.metadata = {
row: row
rowCount: @tokenizedLines.length
@@ -391,7 +400,7 @@ class TokenizedBuffer extends Model
loop
break if scopes.pop() is matchingStartTag
if scopes.length is 0
- atom.assert false, "Encountered an unmatched scope end tag.", (error) =>
+ @assert false, "Encountered an unmatched scope end tag.", (error) =>
error.metadata = {
grammarScopeName: @grammar.scopeName
unmatchedEndTag: @grammar.scopeForId(tag)
@@ -472,13 +481,13 @@ class TokenizedBuffer extends Model
position = Point.fromObject(position)
{openScopes, tags} = @tokenizedLineForRow(position.row)
- scopes = openScopes.map (tag) -> atom.grammars.scopeForId(tag)
+ scopes = openScopes.map (tag) => @grammarRegistry.scopeForId(tag)
startColumn = 0
for tag, tokenIndex in tags
if tag < 0
if tag % 2 is -1
- scopes.push(atom.grammars.scopeForId(tag))
+ scopes.push(@grammarRegistry.scopeForId(tag))
else
scopes.pop()
else
@@ -498,7 +507,7 @@ class TokenizedBuffer extends Model
if tag % 2 is -1
startScopes.pop()
else
- startScopes.push(atom.grammars.scopeForId(tag))
+ startScopes.push(@grammarRegistry.scopeForId(tag))
else
break unless selectorMatchesAnyScope(selector, startScopes)
startColumn -= tag
@@ -508,7 +517,7 @@ class TokenizedBuffer extends Model
tag = tags[endTokenIndex]
if tag < 0
if tag % 2 is -1
- endScopes.push(atom.grammars.scopeForId(tag))
+ endScopes.push(@grammarRegistry.scopeForId(tag))
else
endScopes.pop()
else
diff --git a/src/tooltip-manager.coffee b/src/tooltip-manager.coffee
index 21b8b07c6..247437535 100644
--- a/src/tooltip-manager.coffee
+++ b/src/tooltip-manager.coffee
@@ -54,6 +54,8 @@ class TooltipManager
placement: 'auto top'
viewportPadding: 2
+ constructor: ({@keymapManager}) ->
+
# Essential: Add a tooltip to the given element.
#
# * `target` An `HTMLElement`
@@ -81,7 +83,7 @@ class TooltipManager
{keyBindingCommand, keyBindingTarget} = options
if keyBindingCommand?
- bindings = atom.keymaps.findKeyBindings(command: keyBindingCommand, target: keyBindingTarget)
+ bindings = @keymapManager.findKeyBindings(command: keyBindingCommand, target: keyBindingTarget)
keystroke = getKeystroke(bindings)
if options.title? and keystroke?
options.title += " " + getKeystroke(bindings)
diff --git a/src/view-registry.coffee b/src/view-registry.coffee
index 6af7bd024..3a46aa87a 100644
--- a/src/view-registry.coffee
+++ b/src/view-registry.coffee
@@ -49,15 +49,15 @@ class ViewRegistry
debouncedPerformDocumentPoll: null
minimumPollInterval: 200
- constructor: ->
+ constructor: (@atomEnvironment) ->
+ @observer = new MutationObserver(@requestDocumentPoll)
+ @clear()
+
+ clear: ->
@views = new WeakMap
@providers = []
- @documentWriters = []
- @documentReaders = []
- @documentPollers = []
-
- @observer = new MutationObserver(@requestDocumentPoll)
@debouncedPerformDocumentPoll = _.throttle(@performDocumentPoll, @minimumPollInterval).bind(this)
+ @clearDocumentRequests()
# Essential: Add a provider that will be used to construct views in the
# workspace's view layer based on model objects in its model layer.
@@ -159,7 +159,7 @@ class ViewRegistry
else if object?.jquery
object[0]
else if provider = @findProvider(object)
- element = provider.createView?(object)
+ element = provider.createView?(object, @atomEnvironment)
unless element?
element = new provider.viewConstructor
element.initialize?(object) ? element.setModel?(object)
diff --git a/src/window-bootstrap.coffee b/src/window-bootstrap.coffee
deleted file mode 100644
index 886ba26dc..000000000
--- a/src/window-bootstrap.coffee
+++ /dev/null
@@ -1,13 +0,0 @@
-# Like sands through the hourglass, so are the days of our lives.
-require './window'
-
-Atom = require './atom'
-window.atom = Atom.loadOrCreate('editor')
-atom.initialize()
-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)
diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee
index 53c2aa2e2..bff5ee5b7 100644
--- a/src/window-event-handler.coffee
+++ b/src/window-event-handler.coffee
@@ -1,45 +1,38 @@
path = require 'path'
{Disposable, CompositeDisposable} = require 'event-kit'
-ipc = require 'ipc'
-shell = require 'shell'
fs = require 'fs-plus'
listen = require './delegated-listener'
-# Handles low-level events related to the window.
+# Handles low-level events related to the @window.
module.exports =
class WindowEventHandler
- constructor: ->
+ constructor: ({@atomEnvironment, @applicationDelegate, @window, @document}) ->
@reloadRequested = false
@subscriptions = new CompositeDisposable
- @on(ipc, 'message', @handleIPCMessage)
- @on(ipc, 'command', @handleIPCCommand)
- @on(ipc, 'context-command', @handleIPCContextCommand)
+ @previousOnbeforeunloadHandler = @window.onbeforeunload
+ @window.onbeforeunload = @handleWindowBeforeunload
+ @addEventListener(@window, 'focus', @handleWindowFocus)
+ @addEventListener(@window, 'blur', @handleWindowBlur)
- @previousOnbeforeunloadHandler = window.onbeforeunload
- window.onbeforeunload = @handleWindowBeforeunload
- @addEventListener(window, 'focus', @handleWindowFocus)
- @addEventListener(window, 'blur', @handleWindowBlur)
- @addEventListener(window, 'unload', @handleWindowUnload)
+ @addEventListener(@document, 'keydown', @handleDocumentKeydown)
+ @addEventListener(@document, 'drop', @handleDocumentDrop)
+ @addEventListener(@document, 'dragover', @handleDocumentDragover)
+ @addEventListener(@document, 'contextmenu', @handleDocumentContextmenu)
+ @subscriptions.add listen(@document, 'click', 'a', @handleLinkClick)
+ @subscriptions.add listen(@document, 'submit', 'form', @handleFormSubmit)
- @addEventListener(document, 'keydown', @handleDocumentKeydown)
- @addEventListener(document, 'drop', @handleDocumentDrop)
- @addEventListener(document, 'dragover', @handleDocumentDragover)
- @addEventListener(document, 'contextmenu', @handleDocumentContextmenu)
- @subscriptions.add listen(document, 'click', 'a', @handleLinkClick)
- @subscriptions.add listen(document, 'submit', 'form', @handleFormSubmit)
-
- @subscriptions.add atom.commands.add window,
+ @subscriptions.add @atomEnvironment.commands.add @window,
'window:toggle-full-screen': @handleWindowToggleFullScreen
'window:close': @handleWindowClose
'window:reload': @handleWindowReload
'window:toggle-dev-tools': @handleWindowToggleDevTools
if process.platform in ['win32', 'linux']
- @subscriptions.add atom.commands.add window,
- 'window:toggle-menu-bar': @handleWindowToggleMenuBar
+ @subscriptions.add @atomEnvironment.commands.add @window,
+ '@window:toggle-menu-bar': @handleWindowToggleMenuBar
- @subscriptions.add atom.commands.add document,
+ @subscriptions.add @atomEnvironment.commands.add @document,
'core:focus-next': @handleFocusNext
'core:focus-previous': @handleFocusPrevious
@@ -49,9 +42,9 @@ class WindowEventHandler
# `.native-key-bindings` class.
handleNativeKeybindings: ->
bindCommandToAction = (command, action) =>
- @addEventListener document, command, (event) ->
+ @addEventListener @document, command, (event) =>
if event.target.webkitMatchesSelector('.native-key-bindings')
- atom.getCurrentWindow().webContents[action]()
+ @applicationDelegate.getCurrentWindow().webContents[action]()
bindCommandToAction('core:copy', 'copy')
bindCommandToAction('core:paste', 'paste')
@@ -61,7 +54,7 @@ class WindowEventHandler
bindCommandToAction('core:cut', 'cut')
unsubscribe: ->
- window.onbeforeunload = @previousOnbeforeunloadHandler
+ @window.onbeforeunload = @previousOnbeforeunloadHandler
@subscriptions.dispose()
on: (target, eventName, handler) ->
@@ -74,8 +67,8 @@ class WindowEventHandler
target.addEventListener(eventName, handler)
@subscriptions.add(new Disposable(-> target.removeEventListener(eventName, handler)))
- handleDocumentKeydown: (event) ->
- atom.keymaps.handleKeyboardEvent(event)
+ handleDocumentKeydown: (event) =>
+ @atomEnvironment.keymaps.handleKeyboardEvent(event)
event.stopImmediatePropagation()
handleDrop: (event) ->
@@ -88,14 +81,14 @@ class WindowEventHandler
event.dataTransfer.dropEffect = 'none'
eachTabIndexedElement: (callback) ->
- for element in document.querySelectorAll('[tabindex]')
+ for element in @document.querySelectorAll('[tabindex]')
continue if element.disabled
continue unless element.tabIndex >= 0
callback(element, element.tabIndex)
return
handleFocusNext: =>
- focusedTabIndex = document.activeElement.tabIndex ? -Infinity
+ focusedTabIndex = @document.activeElement.tabIndex ? -Infinity
nextElement = null
nextTabIndex = Infinity
@@ -116,7 +109,7 @@ class WindowEventHandler
lowestElement.focus()
handleFocusPrevious: =>
- focusedTabIndex = document.activeElement.tabIndex ? Infinity
+ focusedTabIndex = @document.activeElement.tabIndex ? Infinity
previousElement = null
previousTabIndex = -Infinity
@@ -136,91 +129,61 @@ class WindowEventHandler
else if highestElement?
highestElement.focus()
- handleIPCMessage: (message, detail) ->
- switch message
- when 'open-locations'
- needsProjectPaths = atom.project?.getPaths().length is 0
-
- for {pathToOpen, initialLine, initialColumn} in detail
- if pathToOpen? and needsProjectPaths
- if fs.existsSync(pathToOpen)
- atom.project.addPath(pathToOpen)
- else if fs.existsSync(path.dirname(pathToOpen))
- atom.project.addPath(path.dirname(pathToOpen))
- else
- atom.project.addPath(pathToOpen)
-
- unless fs.isDirectorySync(pathToOpen)
- atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
- return
- when 'update-available'
- atom.updateAvailable(detail)
-
- handleIPCCommand: (command, args...) ->
- activeElement = document.activeElement
- # Use the workspace element view if body has focus
- if activeElement is document.body and workspaceElement = atom.views.getView(atom.workspace)
- activeElement = workspaceElement
-
- atom.commands.dispatch(activeElement, command, args[0])
-
- handleIPCContextCommand: (command, args...) ->
- atom.commands.dispatch(atom.contextMenu.activeElement, command, args)
-
handleWindowFocus: ->
- document.body.classList.remove('is-blurred')
+ @document.body.classList.remove('is-blurred')
- handleWindowBlur: ->
- document.body.classList.add('is-blurred')
- atom.storeDefaultWindowDimensions()
+ handleWindowBlur: =>
+ @document.body.classList.add('is-blurred')
+ @atomEnvironment.storeDefaultWindowDimensions()
handleWindowBeforeunload: =>
- confirmed = atom.workspace?.confirmClose(windowCloseRequested: true)
- atom.hide() if confirmed and not @reloadRequested and atom.getCurrentWindow().isWebViewFocused()
+ confirmed = @atomEnvironment.workspace?.confirmClose(windowCloseRequested: true)
+ if confirmed and not @reloadRequested and not @atomEnvironment.inSpecMode() and @atomEnvironment.getCurrentWindow().isWebViewFocused()
+ @atomEnvironment.hide()
@reloadRequested = false
- atom.storeDefaultWindowDimensions()
- atom.storeWindowDimensions()
+ @atomEnvironment.storeDefaultWindowDimensions()
+ @atomEnvironment.storeWindowDimensions()
if confirmed
- atom.unloadEditorWindow()
+ @atomEnvironment.unloadEditorWindow()
else
- ipc.send('cancel-window-close')
+ @applicationDelegate.didCancelWindowUnload()
confirmed
- handleWindowUnload: ->
- atom.removeEditorWindow()
+ handleWindowUnload: =>
+ @atomEnvironment.destroy()
- handleWindowToggleFullScreen: ->
- atom.toggleFullScreen()
+ handleWindowToggleFullScreen: =>
+ @atomEnvironment.toggleFullScreen()
- handleWindowClose: ->
- atom.close()
+ handleWindowClose: =>
+ @atomEnvironment.close()
- handleWindowReload: ->
+ handleWindowReload: =>
@reloadRequested = true
- atom.reload()
+ @atomEnvironment.reload()
- handleWindowToggleDevTools: ->
- atom.toggleDevTools()
+ handleWindowToggleDevTools: =>
+ @atomEnvironment.toggleDevTools()
- handleWindowToggleMenuBar: ->
- atom.config.set('core.autoHideMenuBar', not atom.config.get('core.autoHideMenuBar'))
+ handleWindowToggleMenuBar: =>
+ @atomEnvironment.config.set('core.autoHideMenuBar', not @atomEnvironment.config.get('core.autoHideMenuBar'))
- if atom.config.get('core.autoHideMenuBar')
+ if @atomEnvironment.config.get('core.autoHideMenuBar')
detail = "To toggle, press the Alt key or execute the window:toggle-menu-bar command"
- atom.notifications.addInfo('Menu bar hidden', {detail})
+ @atomEnvironment.notifications.addInfo('Menu bar hidden', {detail})
handleLinkClick: (event) ->
event.preventDefault()
- location = event.currentTarget?.getAttribute('href')
- if location and location[0] isnt '#' and /^https?:\/\//.test(location)
- shell.openExternal(location)
+ uri = event.currentTarget?.getAttribute('href')
+ if uri and uri[0] isnt '#' and /^https?:\/\//.test(uri)
+ @applicationDelegate.openExternal(uri)
handleFormSubmit: (event) ->
# Prevent form submits from changing the current window's URL
event.preventDefault()
- handleDocumentContextmenu: (event) ->
+ handleDocumentContextmenu: (event) =>
event.preventDefault()
- atom.contextMenu.showForEvent(event)
+ @atomEnvironment.contextMenu.showForEvent(event)
diff --git a/src/window-load-settings-helpers.coffee b/src/window-load-settings-helpers.coffee
new file mode 100644
index 000000000..59ee2f382
--- /dev/null
+++ b/src/window-load-settings-helpers.coffee
@@ -0,0 +1,20 @@
+remote = require 'remote'
+_ = require 'underscore-plus'
+
+windowLoadSettings = null
+
+exports.getWindowLoadSettings = ->
+ windowLoadSettings ?= JSON.parse(window.decodeURIComponent(window.location.hash.substr(1)))
+ clone = _.deepClone(windowLoadSettings)
+
+ # The windowLoadSettings.windowState could be large, request it only when needed.
+ clone.__defineGetter__ 'windowState', ->
+ remote.getCurrentWindow().loadSettings.windowState
+ clone.__defineSetter__ 'windowState', (value) ->
+ remote.getCurrentWindow().loadSettings.windowState = value
+
+ clone
+
+exports.setWindowLoadSettings = (settings) ->
+ windowLoadSettings = settings
+ location.hash = encodeURIComponent(JSON.stringify(settings))
diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee
index 378969481..fb4b19dc3 100644
--- a/src/workspace-element.coffee
+++ b/src/workspace-element.coffee
@@ -8,18 +8,11 @@ module.exports =
class WorkspaceElement extends HTMLElement
globalTextEditorStyleSheet: null
- createdCallback: ->
- @subscriptions = new CompositeDisposable
- @initializeContent()
- @observeScrollbarStyle()
- @observeTextEditorFontConfig()
-
attachedCallback: ->
@focus()
detachedCallback: ->
@subscriptions.dispose()
- @model.destroy()
initializeContent: ->
@classList.add 'workspace'
@@ -46,31 +39,42 @@ class WorkspaceElement extends HTMLElement
observeTextEditorFontConfig: ->
@updateGlobalTextEditorStyleSheet()
- @subscriptions.add atom.config.onDidChange 'editor.fontSize', @updateGlobalTextEditorStyleSheet.bind(this)
- @subscriptions.add atom.config.onDidChange 'editor.fontFamily', @updateGlobalTextEditorStyleSheet.bind(this)
- @subscriptions.add atom.config.onDidChange 'editor.lineHeight', @updateGlobalTextEditorStyleSheet.bind(this)
+ @subscriptions.add @config.onDidChange 'editor.fontSize', @updateGlobalTextEditorStyleSheet.bind(this)
+ @subscriptions.add @config.onDidChange 'editor.fontFamily', @updateGlobalTextEditorStyleSheet.bind(this)
+ @subscriptions.add @config.onDidChange 'editor.lineHeight', @updateGlobalTextEditorStyleSheet.bind(this)
updateGlobalTextEditorStyleSheet: ->
styleSheetSource = """
atom-text-editor {
- font-size: #{atom.config.get('editor.fontSize')}px;
- font-family: #{atom.config.get('editor.fontFamily')};
- line-height: #{atom.config.get('editor.lineHeight')};
+ font-size: #{@config.get('editor.fontSize')}px;
+ font-family: #{@config.get('editor.fontFamily')};
+ line-height: #{@config.get('editor.lineHeight')};
}
"""
- atom.styles.addStyleSheet(styleSheetSource, sourcePath: 'global-text-editor-styles')
+ @styles.addStyleSheet(styleSheetSource, sourcePath: 'global-text-editor-styles')
- initialize: (@model) ->
- @paneContainer = atom.views.getView(@model.paneContainer)
+ initialize: (@model, {@views, @workspace, @project, @config, @styles}) ->
+ throw new Error("Must pass a views parameter when initializing WorskpaceElements") unless @views?
+ throw new Error("Must pass a workspace parameter when initializing WorskpaceElements") unless @workspace?
+ throw new Error("Must pass a project parameter when initializing WorskpaceElements") unless @project?
+ throw new Error("Must pass a config parameter when initializing WorskpaceElements") unless @config?
+ throw new Error("Must pass a styles parameter when initializing WorskpaceElements") unless @styles?
+
+ @subscriptions = new CompositeDisposable
+ @initializeContent()
+ @observeScrollbarStyle()
+ @observeTextEditorFontConfig()
+
+ @paneContainer = @views.getView(@model.paneContainer)
@verticalAxis.appendChild(@paneContainer)
@addEventListener 'focus', @handleFocus.bind(this)
@panelContainers =
- top: atom.views.getView(@model.panelContainers.top)
- left: atom.views.getView(@model.panelContainers.left)
- right: atom.views.getView(@model.panelContainers.right)
- bottom: atom.views.getView(@model.panelContainers.bottom)
- modal: atom.views.getView(@model.panelContainers.modal)
+ top: @views.getView(@model.panelContainers.top)
+ left: @views.getView(@model.panelContainers.left)
+ right: @views.getView(@model.panelContainers.right)
+ bottom: @views.getView(@model.panelContainers.bottom)
+ modal: @views.getView(@model.panelContainers.modal)
@horizontalAxis.insertBefore(@panelContainers.left, @verticalAxis)
@horizontalAxis.appendChild(@panelContainers.right)
@@ -96,59 +100,10 @@ class WorkspaceElement extends HTMLElement
focusPaneViewOnRight: -> @paneContainer.focusPaneViewOnRight()
runPackageSpecs: ->
- if activePath = atom.workspace.getActivePaneItem()?.getPath?()
- [projectPath] = atom.project.relativizePath(activePath)
+ if activePath = @workspace.getActivePaneItem()?.getPath?()
+ [projectPath] = @project.relativizePath(activePath)
else
- [projectPath] = atom.project.getPaths()
+ [projectPath] = @project.getPaths()
ipc.send('run-package-specs', path.join(projectPath, 'spec')) if projectPath
-atom.commands.add 'atom-workspace',
- '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:run-all-specs': -> ipc.send('command', 'application:run-all-specs')
- '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': -> atom.config.set("editor.showInvisibles", not atom.config.get("editor.showInvisibles"))
- 'window:log-deprecation-warnings': -> Grim.logDeprecations()
- 'window:toggle-auto-indent': -> atom.config.set("editor.autoIndent", not atom.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'
- atom.commands.add 'atom-workspace', 'window:install-shell-commands', -> @getModel().installShellCommands()
-
module.exports = WorkspaceElement = document.registerElement 'atom-workspace', prototype: WorkspaceElement.prototype
diff --git a/src/workspace.coffee b/src/workspace.coffee
index dbd781bd3..b66445bbf 100644
--- a/src/workspace.coffee
+++ b/src/workspace.coffee
@@ -9,10 +9,7 @@ TextEditor = require './text-editor'
PaneContainer = require './pane-container'
Pane = require './pane'
Panel = require './panel'
-PanelElement = require './panel-element'
PanelContainer = require './panel-container'
-PanelContainerElement = require './panel-container-element'
-WorkspaceElement = require './workspace-element'
Task = require './task'
# Essential: Represents the state of the user interface for the entire window.
@@ -26,36 +23,24 @@ Task = require './task'
#
module.exports =
class Workspace extends Model
- atom.deserializers.add(this)
-
- @deserialize: (state) ->
- return unless state?
-
- for packageName in state.packagesWithActiveGrammars ? []
- atom.packages.getLoadedPackage(packageName)?.loadGrammarsSync()
-
- state.paneContainer = PaneContainer.deserialize(state.paneContainer)
- new this(state)
-
constructor: (params) ->
super
- @paneContainer = params?.paneContainer
- @fullScreen = params?.fullScreen ? false
- @destroyedItemURIs = params?.destroyedItemURIs ? []
+ {
+ @packageManager, @config, @project, @grammarRegistry, @notificationManager,
+ @clipboard, @viewRegistry, @grammarRegistry, @applicationDelegate, @assert,
+ @deserializerManager
+ } = params
@emitter = new Emitter
@openers = []
+ @destroyedItemURIs = []
- @paneContainer ?= new PaneContainer()
+ @paneContainer = new PaneContainer({@config, @applicationDelegate, @notificationManager, @deserializerManager})
@paneContainer.onDidDestroyPaneItem(@didDestroyPaneItem)
- @directorySearchers = []
@defaultDirectorySearcher = new DefaultDirectorySearcher()
- atom.packages.serviceHub.consume(
- 'atom.directory-searcher',
- '^0.1.0',
- (provider) => @directorySearchers.unshift(provider))
+ @consumeServices(@packageManager)
@panelContainers =
top: new PanelContainer({location: 'top'})
@@ -64,69 +49,80 @@ class Workspace extends Model
bottom: new PanelContainer({location: 'bottom'})
modal: new PanelContainer({location: 'modal'})
+ @subscribeToEvents()
+
+ reset: (@packageManager) ->
+ @emitter.dispose()
+ @emitter = new Emitter
+
+ @paneContainer.destroy()
+ panelContainer.destroy() for panelContainer in @panelContainers
+
+ @paneContainer = new PaneContainer({@config, @applicationDelegate, @notificationManager, @deserializerManager})
+ @paneContainer.onDidDestroyPaneItem(@didDestroyPaneItem)
+
+ @panelContainers =
+ top: new PanelContainer({location: 'top'})
+ left: new PanelContainer({location: 'left'})
+ right: new PanelContainer({location: 'right'})
+ bottom: new PanelContainer({location: 'bottom'})
+ modal: new PanelContainer({location: 'modal'})
+
+ @originalFontSize = null
+ @openers = []
+ @destroyedItemURIs = []
+ @consumeServices(@packageManager)
+
+ subscribeToEvents: ->
@subscribeToActiveItem()
-
- @addOpener (filePath) ->
- switch filePath
- when 'atom://.atom/stylesheet'
- atom.project.open(atom.styles.getUserStyleSheetPath())
- when 'atom://.atom/keymap'
- atom.project.open(atom.keymaps.getUserKeymapPath())
- when 'atom://.atom/config'
- atom.project.open(atom.config.getUserConfigPath())
- when 'atom://.atom/init-script'
- atom.project.open(atom.getUserInitScriptPath())
-
- atom.views.addViewProvider Workspace, (model) ->
- new WorkspaceElement().initialize(model)
-
- atom.views.addViewProvider PanelContainer, (model) ->
- new PanelContainerElement().initialize(model)
-
- atom.views.addViewProvider Panel, (model) ->
- new PanelElement().initialize(model)
-
@subscribeToFontSize()
+ consumeServices: ({serviceHub}) ->
+ @directorySearchers = []
+ serviceHub.consume(
+ 'atom.directory-searcher',
+ '^0.1.0',
+ (provider) => @directorySearchers.unshift(provider))
+
# Called by the Serializable mixin during serialization.
serialize: ->
deserializer: 'Workspace'
paneContainer: @paneContainer.serialize()
- fullScreen: atom.isFullScreen()
packagesWithActiveGrammars: @getPackageNamesWithActiveGrammars()
+ destroyedItemURIs: @destroyedItemURIs.slice()
+
+ deserialize: (state, deserializerManager) ->
+ for packageName in state.packagesWithActiveGrammars ? []
+ @packageManager.getLoadedPackage(packageName)?.loadGrammarsSync()
+ if state.destroyedItemURIs?
+ @destroyedItemURIs = state.destroyedItemURIs
+ @paneContainer.deserialize(state.paneContainer, deserializerManager)
getPackageNamesWithActiveGrammars: ->
packageNames = []
- addGrammar = ({includedGrammarScopes, packageName}={}) ->
+ addGrammar = ({includedGrammarScopes, packageName}={}) =>
return unless packageName
# Prevent cycles
return if packageNames.indexOf(packageName) isnt -1
packageNames.push(packageName)
for scopeName in includedGrammarScopes ? []
- addGrammar(atom.grammars.grammarForScopeName(scopeName))
+ addGrammar(@grammarRegistry.grammarForScopeName(scopeName))
return
editors = @getTextEditors()
addGrammar(editor.getGrammar()) for editor in editors
if editors.length > 0
- for grammar in atom.grammars.getGrammars() when grammar.injectionSelector
+ for grammar in @grammarRegistry.getGrammars() when grammar.injectionSelector
addGrammar(grammar)
_.uniq(packageNames)
- editorAdded: (editor) ->
-
- installShellCommands: ->
- CommandInstaller = require('./command-installer')
- commandInstaller = new CommandInstaller(atom.getVersion())
- commandInstaller.installShellCommandsInteractively()
-
subscribeToActiveItem: ->
@updateWindowTitle()
@updateDocumentEdited()
- atom.project.onDidChangePaths @updateWindowTitle
+ @project.onDidChangePaths @updateWindowTitle
@observeActivePaneItem (item) =>
@updateWindowTitle()
@@ -156,7 +152,7 @@ class Workspace extends Model
# open.
updateWindowTitle: =>
appName = 'Atom'
- projectPaths = atom.project?.getPaths() ? []
+ projectPaths = @project.getPaths() ? []
if item = @getActivePaneItem()
itemPath = item.getPath?()
itemTitle = item.getTitle?()
@@ -167,19 +163,19 @@ class Workspace extends Model
if item? and projectPath?
document.title = "#{itemTitle} - #{projectPath} - #{appName}"
- atom.setRepresentedFilename(itemPath ? projectPath)
+ @applicationDelegate.setRepresentedFilename(itemPath ? projectPath)
else if projectPath?
document.title = "#{projectPath} - #{appName}"
- atom.setRepresentedFilename(projectPath)
+ @applicationDelegate.setRepresentedFilename(projectPath)
else
document.title = "#{itemTitle} - #{appName}"
- atom.setRepresentedFilename("")
+ @applicationDelegate.setRepresentedFilename("")
# On OS X, fades the application window's proxy icon when the current file
# has been modified.
updateDocumentEdited: =>
modified = @getActivePaneItem()?.isModified?() ? false
- atom.setDocumentEdited(modified)
+ @applicationDelegate.setWindowDocumentEdited(modified)
###
Section: Event Subscription
@@ -391,6 +387,8 @@ class Workspace extends Model
# item will be opened in the rightmost pane of the current active pane's row.
# * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
# containing pane. Defaults to `true`.
+ # * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}
+ # on containing pane. Defaults to `true`.
# * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to
# activate an existing item for the given URI on any pane.
# If `false`, only the active pane will be searched for
@@ -400,7 +398,7 @@ class Workspace extends Model
open: (uri, options={}) ->
searchAllPanes = options.searchAllPanes
split = options.split
- uri = atom.project.resolvePath(uri)
+ uri = @project.resolvePath(uri)
pane = @paneContainer.paneForURI(uri) if searchAllPanes
pane ?= switch split
@@ -429,50 +427,51 @@ class Workspace extends Model
# initially. Defaults to `0`.
# * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on
# the containing pane. Defaults to `true`.
+ # * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem}
+ # on containing pane. Defaults to `true`.
openSync: (uri='', options={}) ->
{initialLine, initialColumn} = options
activatePane = options.activatePane ? true
+ activateItem = options.activateItem ? true
- uri = atom.project.resolvePath(uri)
+ uri = @project.resolvePath(uri)
item = @getActivePane().itemForURI(uri)
if uri
item ?= opener(uri, options) for opener in @getOpeners() when not item
- item ?= atom.project.openSync(uri, {initialLine, initialColumn})
+ item ?= @project.openSync(uri, {initialLine, initialColumn})
- @getActivePane().activateItem(item)
+ @getActivePane().activateItem(item) if activateItem
@itemOpened(item)
@getActivePane().activate() if activatePane
item
openURIInPane: (uri, pane, options={}) ->
activatePane = options.activatePane ? true
+ activateItem = options.activateItem ? true
if uri?
item = pane.itemForURI(uri)
item ?= opener(uri, options) for opener in @getOpeners() when not item
try
- item ?= atom.project.open(uri, options)
+ item ?= @openTextFile(uri, options)
catch error
switch error.code
when 'CANCELLED'
return Promise.resolve()
when 'EACCES'
- atom.notifications.addWarning("Permission denied '#{error.path}'")
+ @notificationManager.addWarning("Permission denied '#{error.path}'")
return Promise.resolve()
when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL'
- atom.notifications.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message)
+ @notificationManager.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message)
return Promise.resolve()
else
throw error
Promise.resolve(item)
.then (item) =>
- if not pane
- pane = new Pane(items: [item])
- @paneContainer.root = pane
@itemOpened(item)
- pane.activateItem(item)
+ pane.activateItem(item) if activateItem
pane.activate() if activatePane
initialLine = initialColumn = 0
@@ -487,6 +486,42 @@ class Workspace extends Model
@emitter.emit 'did-open', {uri, pane, item, index}
item
+ openTextFile: (uri, options) ->
+ filePath = @project.resolvePath(uri)
+
+ 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'
+
+ fileSize = fs.getSizeSync(filePath)
+
+ largeFileMode = fileSize >= 2 * 1048576 # 2MB
+ if fileSize >= 20 * 1048576 # 20MB
+ choice = @applicationDelegate.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
+
+ @project.bufferForPath(filePath, options).then (buffer) =>
+ @buildTextEditor(_.extend({buffer, largeFileMode}, options))
+
+ # Extended: Create a new text editor.
+ #
+ # Returns a {TextEditor}.
+ buildTextEditor: (params) ->
+ params = _.extend({
+ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry,
+ @grammarRegistry, @project, @assert, @applicationDelegate
+ }, params)
+ new TextEditor(params)
+
# Public: Asynchronously reopens the last-closed item's URI if it hasn't already been
# reopened.
#
@@ -640,20 +675,20 @@ class Workspace extends Model
# Increase the editor font size by 1px.
increaseFontSize: ->
- atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)
+ @config.set("editor.fontSize", @config.get("editor.fontSize") + 1)
# Decrease the editor font size by 1px.
decreaseFontSize: ->
- fontSize = atom.config.get("editor.fontSize")
- atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1
+ fontSize = @config.get("editor.fontSize")
+ @config.set("editor.fontSize", fontSize - 1) if fontSize > 1
# Restore to the window's original editor font size.
resetFontSize: ->
if @originalFontSize
- atom.config.set("editor.fontSize", @originalFontSize)
+ @config.set("editor.fontSize", @originalFontSize)
subscribeToFontSize: ->
- atom.config.onDidChange 'editor.fontSize', ({oldValue}) =>
+ @config.onDidChange 'editor.fontSize', ({oldValue}) =>
@originalFontSize ?= oldValue
# Removes the item's uri from the list of potential items to reopen.
@@ -831,7 +866,7 @@ class Workspace extends Model
# Find a searcher for every Directory in the project. Each searcher that is matched
# will be associated with an Array of Directory objects in the Map.
directoriesForSearcher = new Map()
- for directory in atom.project.getDirectories()
+ for directory in @project.getDirectories()
searcher = @defaultDirectorySearcher
for directorySearcher in @directorySearchers
if directorySearcher.canSearchDirectory(directory)
@@ -862,15 +897,15 @@ class Workspace extends Model
# Kick off all of the searches and unify them into one Promise.
allSearches = []
- directoriesForSearcher.forEach (directories, searcher) ->
+ directoriesForSearcher.forEach (directories, searcher) =>
searchOptions =
inclusions: options.paths or []
includeHidden: true
- excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths')
- exclusions: atom.config.get('core.ignoredNames')
- follow: atom.config.get('core.followSymlinks')
- didMatch: (result) ->
- iterator(result) unless atom.project.isPathModified(result.filePath)
+ excludeVcsIgnores: @config.get('core.excludeVcsIgnoredPaths')
+ exclusions: @config.get('core.ignoredNames')
+ follow: @config.get('core.followSymlinks')
+ didMatch: (result) =>
+ iterator(result) unless @project.isPathModified(result.filePath)
didError: (error) ->
iterator(null, error)
didSearchPaths: (count) -> onPathsSearched(searcher, count)
@@ -878,9 +913,9 @@ class Workspace extends Model
allSearches.push(directorySearcher)
searchPromise = Promise.all(allSearches)
- for buffer in atom.project.getBuffers() when buffer.isModified()
+ for buffer in @project.getBuffers() when buffer.isModified()
filePath = buffer.getPath()
- continue unless atom.project.contains(filePath)
+ continue unless @project.contains(filePath)
matches = []
buffer.scan regex, (match) -> matches.push match
iterator {filePath, matches} if matches.length > 0
@@ -926,8 +961,8 @@ class Workspace extends Model
#
# Returns a `Promise`.
replace: (regex, replacementText, filePaths, iterator) ->
- new Promise (resolve, reject) ->
- openPaths = (buffer.getPath() for buffer in atom.project.getBuffers())
+ new Promise (resolve, reject) =>
+ openPaths = (buffer.getPath() for buffer in @project.getBuffers())
outOfProcessPaths = _.difference(filePaths, openPaths)
inProcessFinished = not openPaths.length
@@ -946,7 +981,7 @@ class Workspace extends Model
task.on 'replace:path-replaced', iterator
task.on 'replace:file-error', (error) -> iterator(null, error)
- for buffer in atom.project.getBuffers()
+ for buffer in @project.getBuffers()
continue unless buffer.getPath() in filePaths
replacements = buffer.replace(regex, replacementText, iterator)
iterator({filePath: buffer.getPath(), replacements}) if replacements
diff --git a/static/index.js b/static/index.js
index 11527e963..9bd1f7b20 100644
--- a/static/index.js
+++ b/static/index.js
@@ -76,7 +76,7 @@
setupVmCompatibility()
setupCsonCache(CompileCache.getCacheDirectory())
- require(loadSettings.bootstrapScript)
+ require(loadSettings.windowInitializationScript)
require('ipc').sendChannel('window-command', 'window:loaded')
}
@@ -168,7 +168,7 @@
var backgroundStylesheet = document.createElement('style')
backgroundStylesheet.type = 'text/css'
- backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + '; }'
+ backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + ' !important; }'
document.head.appendChild(backgroundStylesheet)
// Remove once the page loads