mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Merge branch 'master' into as-cjk-soft-wrap
# Conflicts: # spec/display-buffer-spec.coffee
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
|
||||
32
package.json
32
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",
|
||||
|
||||
@@ -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'
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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", ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
111
spec/jasmine-test-runner.coffee
Normal file
111
spec/jasmine-test-runner.coffee
Normal file
@@ -0,0 +1,111 @@
|
||||
fs = require 'fs'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
ipc = require 'ipc'
|
||||
|
||||
module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) ->
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
require 'jasmine-tagged'
|
||||
|
||||
# Allow document.title to be assigned in specs without screwing up spec window title
|
||||
documentTitle = null
|
||||
Object.defineProperty document, 'title',
|
||||
get: -> documentTitle
|
||||
set: (title) -> documentTitle = title
|
||||
|
||||
ApplicationDelegate = require '../src/application-delegate'
|
||||
applicationDelegate = new ApplicationDelegate()
|
||||
applicationDelegate.setRepresentedFilename = ->
|
||||
applicationDelegate.setWindowDocumentEdited = ->
|
||||
window.atom = buildAtomEnvironment({
|
||||
applicationDelegate, window, document,
|
||||
configDirPath: process.env.ATOM_HOME
|
||||
enablePersistence: false
|
||||
})
|
||||
|
||||
require './spec-helper'
|
||||
disableFocusMethods() if process.env.JANKY_SHA1 or process.env.CI
|
||||
requireSpecs(testPath) for testPath in testPaths
|
||||
|
||||
setSpecType('user')
|
||||
|
||||
resolveWithExitCode = null
|
||||
promise = new Promise (resolve, reject) -> resolveWithExitCode = resolve
|
||||
jasmineEnv = jasmine.getEnv()
|
||||
jasmineEnv.addReporter(buildReporter({logFile, headless, resolveWithExitCode}))
|
||||
TimeReporter = require './time-reporter'
|
||||
jasmineEnv.addReporter(new TimeReporter())
|
||||
jasmineEnv.setIncludedTags([process.platform])
|
||||
|
||||
jasmineContent = document.createElement('div')
|
||||
jasmineContent.setAttribute('id', 'jasmine-content')
|
||||
|
||||
document.body.appendChild(jasmineContent)
|
||||
|
||||
jasmineEnv.execute()
|
||||
promise
|
||||
|
||||
disableFocusMethods = ->
|
||||
['fdescribe', 'ffdescribe', 'fffdescribe', 'fit', 'ffit', 'fffit'].forEach (methodName) ->
|
||||
focusMethod = window[methodName]
|
||||
window[methodName] = (description) ->
|
||||
error = new Error('Focused spec is running on CI')
|
||||
focusMethod description, -> throw error
|
||||
|
||||
requireSpecs = (testPath, specType) ->
|
||||
if fs.isDirectorySync(testPath)
|
||||
for testFilePath in fs.listTreeSync(testPath) when /-spec\.(coffee|js)$/.test testFilePath
|
||||
require(testFilePath)
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
setSpecDirectory(testPath)
|
||||
else
|
||||
require(testPath)
|
||||
setSpecDirectory(path.dirname(testPath))
|
||||
|
||||
setSpecField = (name, value) ->
|
||||
specs = jasmine.getEnv().currentRunner().specs()
|
||||
return if specs.length is 0
|
||||
for index in [specs.length-1..0]
|
||||
break if specs[index][name]?
|
||||
specs[index][name] = value
|
||||
|
||||
setSpecType = (specType) ->
|
||||
setSpecField('specType', specType)
|
||||
|
||||
setSpecDirectory = (specDirectory) ->
|
||||
setSpecField('specDirectory', specDirectory)
|
||||
|
||||
buildReporter = ({logFile, headless, resolveWithExitCode}) ->
|
||||
if headless
|
||||
buildTerminalReporter(logFile, resolveWithExitCode)
|
||||
else
|
||||
AtomReporter = require './atom-reporter'
|
||||
reporter = new AtomReporter()
|
||||
|
||||
buildTerminalReporter = (logFile, resolveWithExitCode) ->
|
||||
logStream = fs.openSync(logFile, 'w') if logFile?
|
||||
log = (str) ->
|
||||
if logStream?
|
||||
fs.writeSync(logStream, str)
|
||||
else
|
||||
ipc.send 'write-to-stderr', str
|
||||
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
new TerminalReporter
|
||||
print: (str) ->
|
||||
log(str)
|
||||
onComplete: (runner) ->
|
||||
fs.closeSync(logStream) if logStream?
|
||||
if process.env.JANKY_SHA1 or process.env.CI
|
||||
grim = require 'grim'
|
||||
|
||||
if grim.getDeprecationsLength() > 0
|
||||
grim.logDeprecations()
|
||||
resolveWithExitCode(1)
|
||||
return
|
||||
|
||||
if runner.results().failedCount > 0
|
||||
resolveWithExitCode(1)
|
||||
else
|
||||
resolveWithExitCode(0)
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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", ->
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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')])
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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())
|
||||
|
||||
1
spec/sample-with-comments.js
Normal file
1
spec/sample-with-comments.js
Normal file
@@ -0,0 +1 @@
|
||||
undefined
|
||||
1
spec/sample.js
Normal file
1
spec/sample.js
Normal file
@@ -0,0 +1 @@
|
||||
undefined
|
||||
@@ -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 ->
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -4,7 +4,7 @@ describe "StyleManager", ->
|
||||
[manager, addEvents, removeEvents, updateEvents] = []
|
||||
|
||||
beforeEach ->
|
||||
manager = new StyleManager
|
||||
manager = new StyleManager(configDirPath: atom.getConfigDirPath())
|
||||
addEvents = []
|
||||
removeEvents = []
|
||||
updateEvents = []
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -27,7 +27,9 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
it "stops tokenization", ->
|
||||
@@ -39,7 +41,9 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the buffer contains soft-tabs", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
@@ -345,7 +349,9 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@@ -450,7 +456,9 @@ describe "TokenizedBuffer", ->
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@@ -487,7 +495,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
@@ -500,7 +508,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
@@ -518,7 +526,7 @@ describe "TokenizedBuffer", ->
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
atom.workspace.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
@@ -544,7 +552,9 @@ describe "TokenizedBuffer", ->
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
@@ -566,7 +576,9 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 0]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1, 1]).scopes).toEqual ["source.js"]
|
||||
@@ -575,7 +587,10 @@ describe "TokenizedBuffer", ->
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars,
|
||||
packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the selector does not match the token at the position", ->
|
||||
@@ -595,7 +610,9 @@ describe "TokenizedBuffer", ->
|
||||
it "updates the tab length of the tokenized lines", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
@@ -604,7 +621,9 @@ describe "TokenizedBuffer", ->
|
||||
it "does not allow the tab length to be less than 1", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 1)
|
||||
@@ -617,7 +636,9 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "updates the tokens with the appropriate invisible characters", ->
|
||||
buffer = new TextBuffer(text: " \t a line with tabs\tand \tspaces \t ")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
@@ -630,7 +651,9 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "assigns endOfLineInvisibles to tokenized lines", ->
|
||||
buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
atom.config.set("editor.invisibles", cr: 'R', eol: 'N')
|
||||
@@ -651,7 +674,9 @@ describe "TokenizedBuffer", ->
|
||||
describe "leading and trailing whitespace", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
|
||||
@@ -709,7 +734,9 @@ describe "TokenizedBuffer", ->
|
||||
describe ".indentLevel on tokenized lines", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the line is non-empty", ->
|
||||
@@ -804,7 +831,9 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.insert [10, 0], " // multi-line\n // comment\n // block\n"
|
||||
buffer.insert [0, 0], "// multi-line\n// comment\n// block\n"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedBuffer.onDidChange (change) ->
|
||||
delete change.bufferChange
|
||||
@@ -881,7 +910,9 @@ describe "TokenizedBuffer", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.will-use-the-null-grammar')
|
||||
buffer.setText('a\nb\nc')
|
||||
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizeCallback = jasmine.createSpy('onDidTokenize')
|
||||
tokenizedBuffer.onDidTokenize(tokenizeCallback)
|
||||
|
||||
@@ -905,7 +936,7 @@ describe "TokenizedBuffer", ->
|
||||
registration = atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', -> called = true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js', autoIndent: false).then (o) ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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')
|
||||
|
||||
165
src/application-delegate.coffee
Normal file
165
src/application-delegate.coffee
Normal file
@@ -0,0 +1,165 @@
|
||||
_ = require 'underscore-plus'
|
||||
ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
shell = require 'shell'
|
||||
{Disposable} = require 'event-kit'
|
||||
{getWindowLoadSettings, setWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
|
||||
module.exports =
|
||||
class ApplicationDelegate
|
||||
open: (params) ->
|
||||
ipc.send('open', params)
|
||||
|
||||
pickFolder: (callback) ->
|
||||
responseChannel = "atom-pick-folder-response"
|
||||
ipc.on responseChannel, (path) ->
|
||||
ipc.removeAllListeners(responseChannel)
|
||||
callback(path)
|
||||
ipc.send("pick-folder", responseChannel)
|
||||
|
||||
getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
closeWindow: ->
|
||||
ipc.send("call-window-method", "close")
|
||||
|
||||
getWindowSize: ->
|
||||
[width, height] = remote.getCurrentWindow().getSize()
|
||||
{width, height}
|
||||
|
||||
setWindowSize: (width, height) ->
|
||||
remote.getCurrentWindow().setSize(width, height)
|
||||
|
||||
getWindowPosition: ->
|
||||
[x, y] = remote.getCurrentWindow().getPosition()
|
||||
{x, y}
|
||||
|
||||
setWindowPosition: (x, y) ->
|
||||
ipc.send("call-window-method", "setPosition", x, y)
|
||||
|
||||
centerWindow: ->
|
||||
ipc.send("call-window-method", "center")
|
||||
|
||||
focusWindow: ->
|
||||
ipc.send("call-window-method", "focus")
|
||||
|
||||
showWindow: ->
|
||||
ipc.send("call-window-method", "show")
|
||||
|
||||
hideWindow: ->
|
||||
ipc.send("call-window-method", "hide")
|
||||
|
||||
restartWindow: ->
|
||||
ipc.send("call-window-method", "restart")
|
||||
|
||||
isWindowMaximized: ->
|
||||
remote.getCurrentWindow().isMaximized()
|
||||
|
||||
maximizeWindow: ->
|
||||
ipc.send("call-window-method", "maximize")
|
||||
|
||||
isWindowFullScreen: ->
|
||||
remote.getCurrentWindow().isFullScreen()
|
||||
|
||||
setWindowFullScreen: (fullScreen=false) ->
|
||||
ipc.send("call-window-method", "setFullScreen", fullScreen)
|
||||
|
||||
openWindowDevTools: ->
|
||||
remote.getCurrentWindow().openDevTools()
|
||||
|
||||
toggleWindowDevTools: ->
|
||||
remote.getCurrentWindow().toggleDevTools()
|
||||
|
||||
executeJavaScriptInWindowDevTools: (code) ->
|
||||
remote.getCurrentWindow().executeJavaScriptInDevTools(code)
|
||||
|
||||
setWindowDocumentEdited: (edited) ->
|
||||
ipc.send("call-window-method", "setDocumentEdited", edited)
|
||||
|
||||
setRepresentedFilename: (filename) ->
|
||||
ipc.send("call-window-method", "setRepresentedFilename", filename)
|
||||
|
||||
setRepresentedDirectoryPaths: (paths) ->
|
||||
loadSettings = getWindowLoadSettings()
|
||||
loadSettings['initialPaths'] = paths
|
||||
setWindowLoadSettings(loadSettings)
|
||||
|
||||
setAutoHideWindowMenuBar: (autoHide) ->
|
||||
ipc.send("call-window-method", "setAutoHideMenuBar", autoHide)
|
||||
|
||||
setWindowMenuBarVisibility: (visible) ->
|
||||
remote.getCurrentWindow().setMenuBarVisibility(visible)
|
||||
|
||||
getPrimaryDisplayWorkAreaSize: ->
|
||||
screen = remote.require 'screen'
|
||||
screen.getPrimaryDisplay().workAreaSize
|
||||
|
||||
confirm: ({message, detailedMessage, buttons}) ->
|
||||
buttons ?= {}
|
||||
if _.isArray(buttons)
|
||||
buttonLabels = buttons
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
dialog = remote.require('dialog')
|
||||
chosen = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttonLabels
|
||||
})
|
||||
|
||||
if _.isArray(buttons)
|
||||
chosen
|
||||
else
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
showMessageDialog: (params) ->
|
||||
|
||||
showSaveDialog: (params) ->
|
||||
if _.isString(params)
|
||||
params = defaultPath: params
|
||||
else
|
||||
params = _.clone(params)
|
||||
params.title ?= 'Save File'
|
||||
params.defaultPath ?= getWindowLoadSettings().initialPaths[0]
|
||||
dialog = remote.require('dialog')
|
||||
dialog.showSaveDialog remote.getCurrentWindow(), params
|
||||
|
||||
playBeepSound: ->
|
||||
shell.beep()
|
||||
|
||||
onDidOpenLocations: (callback) ->
|
||||
outerCallback = (message, detail) ->
|
||||
if message is 'open-locations'
|
||||
callback(detail)
|
||||
|
||||
ipc.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipc.removeEventListener('message', outerCallback)
|
||||
|
||||
onUpdateAvailable: (callback) ->
|
||||
outerCallback = (message, detail) ->
|
||||
if message is 'update-available'
|
||||
callback(detail)
|
||||
|
||||
ipc.on('message', outerCallback)
|
||||
new Disposable ->
|
||||
ipc.removeEventListener('message', outerCallback)
|
||||
|
||||
onApplicationMenuCommand: (callback) ->
|
||||
ipc.on('command', callback)
|
||||
new Disposable ->
|
||||
ipc.removeEventListener('command', callback)
|
||||
|
||||
onContextMenuCommand: (callback) ->
|
||||
ipc.on('context-command', callback)
|
||||
new Disposable ->
|
||||
ipc.removeEventListener('context-command', callback)
|
||||
|
||||
didCancelWindowUnload: ->
|
||||
ipc.send('did-cancel-window-unload')
|
||||
|
||||
openExternal: (url) ->
|
||||
shell.openExternal(url)
|
||||
@@ -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) ->
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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."
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: ->
|
||||
|
||||
@@ -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}]+"
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
34
src/initialize-application-window.coffee
Normal file
34
src/initialize-application-window.coffee
Normal file
@@ -0,0 +1,34 @@
|
||||
# Like sands through the hourglass, so are the days of our lives.
|
||||
|
||||
path = require 'path'
|
||||
require './window'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
|
||||
{resourcePath, isSpec, devMode} = getWindowLoadSettings()
|
||||
|
||||
# Add application-specific exports to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
process.env.NODE_PATH = exportsPath
|
||||
|
||||
# Make React faster
|
||||
process.env.NODE_ENV ?= 'production' unless devMode
|
||||
|
||||
AtomEnvironment = require './atom-environment'
|
||||
ApplicationDelegate = require './application-delegate'
|
||||
window.atom = new AtomEnvironment({
|
||||
window, document,
|
||||
applicationDelegate: new ApplicationDelegate,
|
||||
configDirPath: process.env.ATOM_HOME
|
||||
enablePersistence: true
|
||||
})
|
||||
|
||||
atom.displayWindow()
|
||||
atom.loadStateSync()
|
||||
atom.startEditorWindow()
|
||||
|
||||
# Workaround for focus getting cleared upon window creation
|
||||
windowFocused = ->
|
||||
window.removeEventListener('focus', windowFocused)
|
||||
setTimeout (-> document.querySelector('atom-workspace').focus()), 0
|
||||
window.addEventListener('focus', windowFocused)
|
||||
59
src/initialize-test-window.coffee
Normal file
59
src/initialize-test-window.coffee
Normal file
@@ -0,0 +1,59 @@
|
||||
# Start the crash reporter before anything else.
|
||||
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
|
||||
remote = require 'remote'
|
||||
|
||||
exitWithStatusCode = (status) ->
|
||||
remote.require('app').emit('will-quit')
|
||||
remote.process.exit(status)
|
||||
|
||||
try
|
||||
path = require 'path'
|
||||
ipc = require 'ipc'
|
||||
{getWindowLoadSettings} = require './window-load-settings-helpers'
|
||||
AtomEnvironment = require '../src/atom-environment'
|
||||
ApplicationDelegate = require '../src/application-delegate'
|
||||
|
||||
# Show window synchronously so a focusout doesn't fire on input elements
|
||||
# that are focused in the very first spec run.
|
||||
remote.getCurrentWindow().show() unless getWindowLoadSettings().headless
|
||||
|
||||
handleKeydown = (event) ->
|
||||
# Reload: cmd-r / ctrl-r
|
||||
if (event.metaKey or event.ctrlKey) and event.keyCode is 82
|
||||
ipc.send('call-window-method', 'restart')
|
||||
|
||||
# Toggle Dev Tools: cmd-alt-i / ctrl-alt-i
|
||||
if (event.metaKey or event.ctrlKey) and event.altKey and event.keyCode is 73
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Reload: cmd-w / ctrl-w
|
||||
if (event.metaKey or event.ctrlKey) and event.keyCode is 87
|
||||
ipc.send('call-window-method', 'close')
|
||||
|
||||
window.addEventListener('keydown', handleKeydown, true)
|
||||
|
||||
# Add 'exports' to module search path.
|
||||
exportsPath = path.join(getWindowLoadSettings().resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it.
|
||||
|
||||
document.title = "Spec Suite"
|
||||
|
||||
legacyTestRunner = require(getWindowLoadSettings().legacyTestRunnerPath)
|
||||
testRunner = require(getWindowLoadSettings().testRunnerPath)
|
||||
promise = testRunner({
|
||||
logFile: getWindowLoadSettings().logFile
|
||||
headless: getWindowLoadSettings().headless
|
||||
testPaths: getWindowLoadSettings().testPaths
|
||||
buildAtomEnvironment: (params) -> new AtomEnvironment(params)
|
||||
buildDefaultApplicationDelegate: (params) -> new ApplicationDelegate()
|
||||
legacyTestRunner: legacyTestRunner
|
||||
})
|
||||
|
||||
promise.then(exitWithStatusCode) if getWindowLoadSettings().headless
|
||||
catch error
|
||||
if getWindowLoadSettings().headless
|
||||
console.error(error.stack ? error)
|
||||
exitWithStatusCode(1)
|
||||
else
|
||||
throw error
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
|
||||
@@ -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: {}}
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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\+/, '')
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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()}`.
|
||||
""",
|
||||
|
||||
212
src/register-default-commands.coffee
Normal file
212
src/register-default-commands.coffee
Normal file
@@ -0,0 +1,212 @@
|
||||
ipc = require 'ipc'
|
||||
|
||||
module.exports = ({commandRegistry, commandInstaller, config}) ->
|
||||
commandRegistry.add 'atom-workspace',
|
||||
'pane:show-next-item': -> @getModel().getActivePane().activateNextItem()
|
||||
'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem()
|
||||
'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0)
|
||||
'pane:show-item-2': -> @getModel().getActivePane().activateItemAtIndex(1)
|
||||
'pane:show-item-3': -> @getModel().getActivePane().activateItemAtIndex(2)
|
||||
'pane:show-item-4': -> @getModel().getActivePane().activateItemAtIndex(3)
|
||||
'pane:show-item-5': -> @getModel().getActivePane().activateItemAtIndex(4)
|
||||
'pane:show-item-6': -> @getModel().getActivePane().activateItemAtIndex(5)
|
||||
'pane:show-item-7': -> @getModel().getActivePane().activateItemAtIndex(6)
|
||||
'pane:show-item-8': -> @getModel().getActivePane().activateItemAtIndex(7)
|
||||
'pane:show-item-9': -> @getModel().getActivePane().activateItemAtIndex(8)
|
||||
'pane:move-item-right': -> @getModel().getActivePane().moveItemRight()
|
||||
'pane:move-item-left': -> @getModel().getActivePane().moveItemLeft()
|
||||
'window:increase-font-size': -> @getModel().increaseFontSize()
|
||||
'window:decrease-font-size': -> @getModel().decreaseFontSize()
|
||||
'window:reset-font-size': -> @getModel().resetFontSize()
|
||||
'application:about': -> ipc.send('command', 'application:about')
|
||||
'application:show-preferences': -> ipc.send('command', 'application:show-settings')
|
||||
'application:show-settings': -> ipc.send('command', 'application:show-settings')
|
||||
'application:quit': -> ipc.send('command', 'application:quit')
|
||||
'application:hide': -> ipc.send('command', 'application:hide')
|
||||
'application:hide-other-applications': -> ipc.send('command', 'application:hide-other-applications')
|
||||
'application:install-update': -> ipc.send('command', 'application:install-update')
|
||||
'application:unhide-all-applications': -> ipc.send('command', 'application:unhide-all-applications')
|
||||
'application:new-window': -> ipc.send('command', 'application:new-window')
|
||||
'application:new-file': -> ipc.send('command', 'application:new-file')
|
||||
'application:open': -> ipc.send('command', 'application:open')
|
||||
'application:open-file': -> ipc.send('command', 'application:open-file')
|
||||
'application:open-folder': -> ipc.send('command', 'application:open-folder')
|
||||
'application:open-dev': -> ipc.send('command', 'application:open-dev')
|
||||
'application:open-safe': -> ipc.send('command', 'application:open-safe')
|
||||
'application:add-project-folder': -> atom.addProjectFolder()
|
||||
'application:minimize': -> ipc.send('command', 'application:minimize')
|
||||
'application:zoom': -> ipc.send('command', 'application:zoom')
|
||||
'application:bring-all-windows-to-front': -> ipc.send('command', 'application:bring-all-windows-to-front')
|
||||
'application:open-your-config': -> ipc.send('command', 'application:open-your-config')
|
||||
'application:open-your-init-script': -> ipc.send('command', 'application:open-your-init-script')
|
||||
'application:open-your-keymap': -> ipc.send('command', 'application:open-your-keymap')
|
||||
'application:open-your-snippets': -> ipc.send('command', 'application:open-your-snippets')
|
||||
'application:open-your-stylesheet': -> ipc.send('command', 'application:open-your-stylesheet')
|
||||
'application:open-license': -> @getModel().openLicense()
|
||||
'window:run-package-specs': -> @runPackageSpecs()
|
||||
'window:focus-next-pane': -> @getModel().activateNextPane()
|
||||
'window:focus-previous-pane': -> @getModel().activatePreviousPane()
|
||||
'window:focus-pane-above': -> @focusPaneViewAbove()
|
||||
'window:focus-pane-below': -> @focusPaneViewBelow()
|
||||
'window:focus-pane-on-left': -> @focusPaneViewOnLeft()
|
||||
'window:focus-pane-on-right': -> @focusPaneViewOnRight()
|
||||
'window:save-all': -> @getModel().saveAll()
|
||||
'window:toggle-invisibles': -> config.set("editor.showInvisibles", not config.get("editor.showInvisibles"))
|
||||
'window:log-deprecation-warnings': -> Grim.logDeprecations()
|
||||
'window:toggle-auto-indent': -> config.set("editor.autoIndent", not config.get("editor.autoIndent"))
|
||||
'pane:reopen-closed-item': -> @getModel().reopenItem()
|
||||
'core:close': -> @getModel().destroyActivePaneItemOrEmptyPane()
|
||||
'core:save': -> @getModel().saveActivePaneItem()
|
||||
'core:save-as': -> @getModel().saveActivePaneItemAs()
|
||||
|
||||
if process.platform is 'darwin'
|
||||
commandRegistry.add 'atom-workspace', 'window:install-shell-commands', ->
|
||||
commandInstaller.installShellCommandsInteractively()
|
||||
|
||||
commandRegistry.add 'atom-pane',
|
||||
'pane:save-items': -> @getModel().saveItems()
|
||||
'pane:split-left': -> @getModel().splitLeft(copyActiveItem: true)
|
||||
'pane:split-right': -> @getModel().splitRight(copyActiveItem: true)
|
||||
'pane:split-up': -> @getModel().splitUp(copyActiveItem: true)
|
||||
'pane:split-down': -> @getModel().splitDown(copyActiveItem: true)
|
||||
'pane:close': -> @getModel().close()
|
||||
'pane:close-other-items': -> @getModel().destroyInactiveItems()
|
||||
'pane:increase-size': -> @getModel().increaseSize()
|
||||
'pane:decrease-size': -> @getModel().decreaseSize()
|
||||
|
||||
commandRegistry.add 'atom-text-editor', stopEventPropagation(
|
||||
'core:undo': -> @undo()
|
||||
'core:redo': -> @redo()
|
||||
'core:move-left': -> @moveLeft()
|
||||
'core:move-right': -> @moveRight()
|
||||
'core:select-left': -> @selectLeft()
|
||||
'core:select-right': -> @selectRight()
|
||||
'core:select-up': -> @selectUp()
|
||||
'core:select-down': -> @selectDown()
|
||||
'core:select-all': -> @selectAll()
|
||||
'editor:select-word': -> @selectWordsContainingCursors()
|
||||
'editor:consolidate-selections': (event) -> event.abortKeyBinding() unless @consolidateSelections()
|
||||
'editor:move-to-beginning-of-next-paragraph': -> @moveToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': -> @moveToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': -> @moveToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': -> @moveToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': -> @moveToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': -> @moveToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': -> @moveToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': -> @moveToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': -> @moveToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': -> @moveToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': -> @moveToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': -> @moveToNextWordBoundary()
|
||||
'editor:move-to-previous-subword-boundary': -> @moveToPreviousSubwordBoundary()
|
||||
'editor:move-to-next-subword-boundary': -> @moveToNextSubwordBoundary()
|
||||
'editor:select-to-beginning-of-next-paragraph': -> @selectToBeginningOfNextParagraph()
|
||||
'editor:select-to-beginning-of-previous-paragraph': -> @selectToBeginningOfPreviousParagraph()
|
||||
'editor:select-to-end-of-line': -> @selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': -> @selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': -> @selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': -> @selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': -> @selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': -> @selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': -> @selectToPreviousWordBoundary()
|
||||
'editor:select-to-next-subword-boundary': -> @selectToNextSubwordBoundary()
|
||||
'editor:select-to-previous-subword-boundary': -> @selectToPreviousSubwordBoundary()
|
||||
'editor:select-to-first-character-of-line': -> @selectToFirstCharacterOfLine()
|
||||
'editor:select-line': -> @selectLinesContainingCursors()
|
||||
)
|
||||
|
||||
commandRegistry.add 'atom-text-editor', stopEventPropagationAndGroupUndo(config,
|
||||
'core:backspace': -> @backspace()
|
||||
'core:delete': -> @delete()
|
||||
'core:cut': -> @cutSelectedText()
|
||||
'core:copy': -> @copySelectedText()
|
||||
'core:paste': -> @pasteText()
|
||||
'editor:delete-to-previous-word-boundary': -> @deleteToPreviousWordBoundary()
|
||||
'editor:delete-to-next-word-boundary': -> @deleteToNextWordBoundary()
|
||||
'editor:delete-to-beginning-of-word': -> @deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': -> @deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-line': -> @deleteToEndOfLine()
|
||||
'editor:delete-to-end-of-word': -> @deleteToEndOfWord()
|
||||
'editor:delete-to-beginning-of-subword': -> @deleteToBeginningOfSubword()
|
||||
'editor:delete-to-end-of-subword': -> @deleteToEndOfSubword()
|
||||
'editor:delete-line': -> @deleteLine()
|
||||
'editor:cut-to-end-of-line': -> @cutToEndOfLine()
|
||||
'editor:cut-to-end-of-buffer-line': -> @cutToEndOfBufferLine()
|
||||
'editor:transpose': -> @transpose()
|
||||
'editor:upper-case': -> @upperCase()
|
||||
'editor:lower-case': -> @lowerCase()
|
||||
'editor:copy-selection': -> @copyOnlySelectedText()
|
||||
)
|
||||
|
||||
commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagation(
|
||||
'core:move-up': -> @moveUp()
|
||||
'core:move-down': -> @moveDown()
|
||||
'core:move-to-top': -> @moveToTop()
|
||||
'core:move-to-bottom': -> @moveToBottom()
|
||||
'core:page-up': -> @pageUp()
|
||||
'core:page-down': -> @pageDown()
|
||||
'core:select-to-top': -> @selectToTop()
|
||||
'core:select-to-bottom': -> @selectToBottom()
|
||||
'core:select-page-up': -> @selectPageUp()
|
||||
'core:select-page-down': -> @selectPageDown()
|
||||
'editor:add-selection-below': -> @addSelectionBelow()
|
||||
'editor:add-selection-above': -> @addSelectionAbove()
|
||||
'editor:split-selections-into-lines': -> @splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': -> @toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': -> @toggleSoftWrapped()
|
||||
'editor:fold-all': -> @foldAll()
|
||||
'editor:unfold-all': -> @unfoldAll()
|
||||
'editor:fold-current-row': -> @foldCurrentRow()
|
||||
'editor:unfold-current-row': -> @unfoldCurrentRow()
|
||||
'editor:fold-selection': -> @foldSelectedLines()
|
||||
'editor:fold-at-indent-level-1': -> @foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': -> @foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': -> @foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': -> @foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': -> @foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': -> @foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': -> @foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': -> @foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': -> @foldAllAtIndentLevel(8)
|
||||
'editor:log-cursor-scope': -> @logCursorScope()
|
||||
'editor:copy-path': -> @copyPathToClipboard()
|
||||
'editor:toggle-indent-guide': -> config.set('editor.showIndentGuide', not config.get('editor.showIndentGuide'))
|
||||
'editor:toggle-line-numbers': -> config.set('editor.showLineNumbers', not config.get('editor.showLineNumbers'))
|
||||
'editor:scroll-to-cursor': -> @scrollToCursorPosition()
|
||||
)
|
||||
|
||||
commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(config,
|
||||
'editor:indent': -> @indent()
|
||||
'editor:auto-indent': -> @autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': -> @indentSelectedRows()
|
||||
'editor:outdent-selected-rows': -> @outdentSelectedRows()
|
||||
'editor:newline': -> @insertNewline()
|
||||
'editor:newline-below': -> @insertNewlineBelow()
|
||||
'editor:newline-above': -> @insertNewlineAbove()
|
||||
'editor:toggle-line-comments': -> @toggleLineCommentsInSelection()
|
||||
'editor:checkout-head-revision': -> @checkoutHeadRevision()
|
||||
'editor:move-line-up': -> @moveLineUp()
|
||||
'editor:move-line-down': -> @moveLineDown()
|
||||
'editor:duplicate-lines': -> @duplicateLines()
|
||||
'editor:join-lines': -> @joinLines()
|
||||
)
|
||||
|
||||
stopEventPropagation = (commandListeners) ->
|
||||
newCommandListeners = {}
|
||||
for commandName, commandListener of commandListeners
|
||||
do (commandListener) ->
|
||||
newCommandListeners[commandName] = (event) ->
|
||||
event.stopPropagation()
|
||||
commandListener.call(@getModel(), event)
|
||||
newCommandListeners
|
||||
|
||||
stopEventPropagationAndGroupUndo = (config, commandListeners) ->
|
||||
newCommandListeners = {}
|
||||
for commandName, commandListener of commandListeners
|
||||
do (commandListener) ->
|
||||
newCommandListeners[commandName] = (event) ->
|
||||
event.stopPropagation()
|
||||
model = @getModel()
|
||||
model.transact config.get('editor.undoGroupingInterval'), ->
|
||||
commandListener.call(model, event)
|
||||
newCommandListeners
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -14,6 +14,7 @@ class StylesElement extends HTMLElement
|
||||
@emitter.on 'did-update-style-element', callback
|
||||
|
||||
createdCallback: ->
|
||||
@subscriptions = new CompositeDisposable
|
||||
@emitter = new Emitter
|
||||
@styleElementClonesByOriginalElement = new WeakMap
|
||||
|
||||
@@ -21,31 +22,29 @@ class StylesElement extends HTMLElement
|
||||
if @context is 'atom-text-editor'
|
||||
for styleElement in @children
|
||||
@upgradeDeprecatedSelectors(styleElement)
|
||||
@initialize()
|
||||
|
||||
@context = @getAttribute('context') ? undefined
|
||||
|
||||
detachedCallback: ->
|
||||
@subscriptions.dispose()
|
||||
@subscriptions = null
|
||||
@subscriptions = new CompositeDisposable
|
||||
|
||||
attributeChangedCallback: (attrName, oldVal, newVal) ->
|
||||
@contextChanged() if attrName is 'context'
|
||||
|
||||
initialize: ->
|
||||
return if @subscriptions?
|
||||
initialize: (@styleManager) ->
|
||||
throw new Error("Must pass a styleManager parameter when initializing a StylesElement") unless @styleManager?
|
||||
|
||||
@subscriptions = new CompositeDisposable
|
||||
@context = @getAttribute('context') ? undefined
|
||||
|
||||
@subscriptions.add atom.styles.observeStyleElements(@styleElementAdded.bind(this))
|
||||
@subscriptions.add atom.styles.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
|
||||
@subscriptions.add atom.styles.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
|
||||
@subscriptions.add @styleManager.observeStyleElements(@styleElementAdded.bind(this))
|
||||
@subscriptions.add @styleManager.onDidRemoveStyleElement(@styleElementRemoved.bind(this))
|
||||
@subscriptions.add @styleManager.onDidUpdateStyleElement(@styleElementUpdated.bind(this))
|
||||
|
||||
contextChanged: ->
|
||||
return unless @subscriptions?
|
||||
|
||||
@styleElementRemoved(child) for child in Array::slice.call(@children)
|
||||
@context = @getAttribute('context')
|
||||
@styleElementAdded(styleElement) for styleElement in atom.styles.getStyleElements()
|
||||
@styleElementAdded(styleElement) for styleElement in @styleManager.getStyleElements()
|
||||
return
|
||||
|
||||
styleElementAdded: (styleElement) ->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user