Comparar commits

...

95 Commits

Autor SHA1 Mensagem Data
Kevin Sawicki 0b2599565e Upgrade to apm 0.101 2014-10-06 13:29:39 -07:00
Nathan Sobo 34bd103c3d Move remaining workspace commands back to $::command temporarily 2014-10-06 13:19:47 -06:00
Nathan Sobo b00441bee7 Register workspace commands via $::command until compatibility is better
This is a temporary measure.

Currently, commands registered via atom.commands.add don’t mesh properly
with the dispatch of commands registered via jQuery. Didn’t think this
was a big deal until I realized that it broke the ability to preempt
commands on ancestor nodes by calling stopPropagation, which might break
packages.
2014-10-06 13:16:36 -06:00
Kevin Sawicki e33e5df467 Upgrade to language-gfm@0.51 2014-10-06 11:16:55 -07:00
Ben Ogle 212fbd915d Add tabLength 3
Closes #3724
2014-10-06 10:46:31 -07:00
Kevin Sawicki 8bd3e848e0 Upgrade to language-ruby@0.39 2014-10-06 10:05:10 -07:00
Kevin Sawicki 400c8f3dcd Upgrade to language-yaml@0.18 2014-10-06 09:24:12 -07:00
Kevin Sawicki fff752d944 Upgrade to language-css@0.18 2014-10-06 08:51:06 -07:00
Kevin Sawicki f843d07403 Add missing CommandInstaller require
Closes #3729
2014-10-06 08:47:44 -07:00
Nathan Sobo d7efa9bb37 Add PaneElement::attached to prevent shape change 2014-10-03 17:51:44 -06:00
Kevin Sawicki d12a2cf284 Unfocus spec 2014-10-03 15:23:54 -07:00
Kevin Sawicki a807619906 Only call show on active item when attached
Closes atom/settings-view#258
2014-10-03 15:23:21 -07:00
Nathan Sobo e3245ec4b8 Remove logging from spec 2014-10-03 15:56:12 -06:00
Kevin Sawicki 4231d69421 Upgrade to apm 0.100 2014-10-03 14:06:12 -07:00
Ben Ogle 64cc7f98ea Merge pull request #3697 from atom/bo-config-scoped-properties
Add scoped settings to config
2014-10-03 14:01:03 -07:00
Ben Ogle 062fa29895 addRawScopedValue -> setRawScopedValue 2014-10-03 11:57:50 -07:00
Ben Ogle 4e3c8406ee Clean up docs 2014-10-03 11:57:35 -07:00
Ben Ogle 47d5b46a1d Fix warnings from schema incorrectness 2014-10-03 11:42:45 -07:00
Ben Ogle a711e908d5 💄 2014-10-03 10:46:57 -07:00
Ben Ogle 27da0669f3 Moar 📝 2014-10-03 10:46:57 -07:00
Ben Ogle 16fd53c123 Add schemas for scoped configs 2014-10-03 10:46:57 -07:00
Ben Ogle 2475e1a9a6 📝 Update docs for scoped settings 2014-10-03 10:46:57 -07:00
Ben Ogle f724c7fca8 Implement observing on scoped properties 2014-10-03 10:46:57 -07:00
Ben Ogle f8a3ae6104 Pull observing out into special methods for global config 2014-10-03 10:46:57 -07:00
Ben Ogle 899929a1ce addScopedDefaults -> addScopedSettings 2014-10-03 10:46:57 -07:00
Ben Ogle e5d67bb2ff Can remove the clearing of scoped properties
Config is being created on each spec run!
2014-10-03 10:46:57 -07:00
Ben Ogle 3732bdf1e9 Ugh, add settingsForScopeDescriptor back
It’s used by language mode and autocomplete for different things
2014-10-03 10:46:57 -07:00
Ben Ogle aedf02a3e3 Remove +default junk 2014-10-03 10:46:57 -07:00
Ben Ogle d47dbede29 Fix specs 2014-10-03 10:46:57 -07:00
Ben Ogle b1f8c6a6e8 Remove special method for language mode 2014-10-03 10:46:57 -07:00
Ben Ogle a8fad6a0fb Use disposables for removing properties 2014-10-03 10:46:13 -07:00
Ben Ogle e2ac19c17f Use config rather than syntax for scoped properties 2014-10-03 10:46:13 -07:00
Ben Ogle 82990cfc77 rename method 2014-10-03 10:46:13 -07:00
Ben Ogle d72b179b3b Use config in spec helper 2014-10-03 10:46:13 -07:00
Ben Ogle 9a957fe0a4 Fix specs for settings view. 2014-10-03 10:46:13 -07:00
Ben Ogle 21feab322f Add deprecations to the syntax scoped property methods 2014-10-03 10:46:13 -07:00
Ben Ogle dd05c6cec1 Syntax calls into atom.config for scoped properties 2014-10-03 10:46:13 -07:00
Ben Ogle f61a7d0c62 Remove unused method 2014-10-03 10:46:13 -07:00
Ben Ogle 7a5054027e Shift the args before coercing the value 2014-10-03 10:46:13 -07:00
Ben Ogle 778d9fafc5 fix value and default value object checks 2014-10-03 10:46:13 -07:00
Ben Ogle 38e889b7d8 Reorganize private methods into section 2014-10-03 10:46:13 -07:00
Ben Ogle 356f4bec7c Basic scoped settings in Config works 2014-10-03 10:46:13 -07:00
Ben Ogle 8533286114 Move internal things into an internal section 2014-10-03 10:46:13 -07:00
Kevin Sawicki 98d31a1d30 💄 Remove some lint 2014-10-02 17:54:23 -07:00
Kevin Sawicki 59aa3a446c Upgrade to apm 0.99 2014-10-02 16:57:16 -07:00
Kevin Sawicki 679031ce83 Upgrade to text-buffer@3.2.8 2014-10-02 16:50:53 -07:00
Kevin Sawicki 3860091c62 Upgrade to scoped-property-store@0.11 2014-10-02 16:46:25 -07:00
Kevin Sawicki 06af3f38c0 Upgrade to tree-view@0.128 2014-10-02 16:37:57 -07:00
Kevin Sawicki da6f664903 Upgrade to snippets@0.55 2014-10-02 16:08:54 -07:00
Nathan Sobo 4c124b8174 Merge pull request #3633 from atom/ns-workspace-custom-elements
Use custom elements for workspace views
2014-10-02 16:00:31 -06:00
Kevin Sawicki 4a818d76d6 Upgrade to language-go@0.18 2014-10-02 14:15:02 -07:00
Kevin Sawicki 811758aec7 Prepare 0.136 2014-10-02 10:56:20 -07:00
Nathan Sobo 5cb31c874f Properly emit item argument in pane:before-item-destroyed legacy event 2014-10-01 17:58:09 -06:00
Nathan Sobo b24e1fa405 Merge branch 'master' into ns-workspace-custom-elements 2014-10-01 17:15:09 -06:00
Nathan Sobo f004f8c45d Merge branch 'master' into ns-workspace-custom-elements
Conflicts:
	spec/pane-view-spec.coffee
	spec/workspace-view-spec.coffee
	src/workspace-view.coffee
	src/workspace.coffee
2014-10-01 11:49:23 -06:00
Nathan Sobo 19ff2bd986 Set the project path is the represented file path if undefined for item
This prevents exceptions on the browser process from passing a null
argument over IPC.
2014-09-29 11:21:33 -06:00
Nathan Sobo be7d093a4a Merge remote-tracking branch 'origin/master' into ns-workspace-custom-elements
Conflicts:
	package.json
2014-09-29 11:13:45 -06:00
Nathan Sobo 0ebedeec3a Merge remote-tracking branch 'origin/master' into ns-workspace-custom-elements
Conflicts:
	package.json
	src/workspace.coffee
2014-09-26 14:49:28 -06:00
Nathan Sobo aa1eb94fa7 Ignore redundant calls to $::attachToDom 2014-09-25 22:17:26 -06:00
Nathan Sobo 8723e69f1c Upgrade status-bar to fix specs 2014-09-25 21:58:44 -06:00
Nathan Sobo b1c5442f93 Upgrade command-palette to fix specs 2014-09-25 21:28:52 -06:00
Nathan Sobo 756a389ccf Don't require Workspace in WorkspaceView 2014-09-25 21:28:09 -06:00
Nathan Sobo 5a72d12026 Don’t use jQuery to attach window listeners in spec-helper
This prevents the command palette spec from failing because it’s
explicitly asserting that no listener registered on the window without
a description should show up in the palette, but core:close is also
registered on the workspace.
2014-09-25 20:57:09 -06:00
Nathan Sobo 06e0919597 Revert "Make $::view return __spacePenView from the first element if present"
This reverts commit edaf1e2ced.
2014-09-25 20:45:18 -06:00
Nathan Sobo 0afe2a55e9 Handle direct construction of WorkspaceView in a better way 2014-09-25 20:40:11 -06:00
Nathan Sobo 3442157e16 Fix command-installer-spec 2014-09-25 20:39:04 -06:00
Nathan Sobo 039d87caa0 Allow WorkspaceView to be instantiated directly for compatibility 2014-09-25 20:17:04 -06:00
Nathan Sobo edaf1e2ced Make $::view return __spacePenView from the first element if present 2014-09-25 20:16:40 -06:00
Nathan Sobo 4b746deb73 Move shell command installation entirely into CommandInstaller 2014-09-25 13:11:15 -06:00
Nathan Sobo 442223f97b 💄 spec language 2014-09-25 12:44:53 -06:00
Nathan Sobo 4207752a08 Rename deprecatedViewEvents to deprecateViewEvents 2014-09-25 12:42:12 -06:00
Nathan Sobo a5781d65c3 Remove methods carried over from old view 2014-09-25 12:36:44 -06:00
Nathan Sobo 9d2bb71109 More cleanup 2014-09-25 12:35:08 -06:00
Nathan Sobo eaa90e6158 Clean up debugging 2014-09-25 12:33:05 -06:00
Nathan Sobo 187e300167 Apply workaround for clearing of focus upon loading of window
After the first window focus event, the focus is getting cleared back
to document.body regardless of the prior active element. Refocusing
workspace on a delay after the first window focus event works around
the problem.
2014-09-25 11:56:29 -06:00
Nathan Sobo 670f3e4946 Add WorkspaceElement 2014-09-25 11:41:09 -06:00
Nathan Sobo ae488fc7fe Update document edited status in workspace model
This also fixes a previous oversight where the status wasn’t updated
when switching between pane items with different modified status.
2014-09-24 17:00:25 -06:00
Nathan Sobo 28deb9dec5 Maintain document.title in the workspace model, not the view 2014-09-24 16:26:54 -06:00
Nathan Sobo 186335d619 Mock out document.title with Object.defineProperty
Previously the WorkspaceView::setTitle was mocked in specs to prevent
the title from changing. But I would like to move the title update logic
without breaking assertions, so now we can assert directly on
document.title.
2014-09-24 15:56:50 -06:00
Nathan Sobo 1c58438124 Move PaneContainerView::confirmClose to the model layer 2014-09-24 15:46:01 -06:00
Nathan Sobo 3e0477ffcb Move shell command installation to workspace model 2014-09-24 15:28:35 -06:00
Nathan Sobo 368ef59b85 Remove stray log 2014-09-24 15:28:34 -06:00
Nathan Sobo 465d2afd95 Remove the old root view properly 2014-09-24 15:28:34 -06:00
Nathan Sobo 32f0eb4f76 Don’t emit repeated attached events for the same PaneView 2014-09-24 15:28:34 -06:00
Nathan Sobo 60a551b308 Only dispose PaneElement subscriptions when pane is destroyed
Not when the element is detached, because it might be reattached.
2014-09-24 15:28:34 -06:00
Nathan Sobo f76f7e17d3 Upgrade space-pen for simulated dom attachment bug fix 2014-09-24 15:28:34 -06:00
Nathan Sobo da87f321e8 Add PaneAxisElement 2014-09-24 15:28:34 -06:00
Nathan Sobo 11ede2d436 Upgrade autosave to fix specs 2014-09-24 15:28:34 -06:00
Nathan Sobo 2cc6c9e4c0 💄 2014-09-24 15:28:34 -06:00
Nathan Sobo 887a7bcaf4 Restore $.fn.element property
We define element in certain space-pen views, but this makes it
available for all jQuery objects. Can’t remove it from the prototype.
2014-09-24 15:28:34 -06:00
Nathan Sobo df37d77895 Remove Pane::getViewClass as we now use a view provider 2014-09-24 15:28:34 -06:00
Nathan Sobo cf8dc29cc5 Use Pane::onDidAddItem instead of ::observeItems
The former includes the index at which the item was added and the latter
does not.
2014-09-24 15:28:33 -06:00
Nathan Sobo de29ca6906 Keep existing PaneView SpacePen API working as before
We will eventually deprecate all access to views via
`atom.workspaceView`, which is the only way to get a reference to
instances of PaneView. Draining the swamp!
2014-09-24 15:28:33 -06:00
Nathan Sobo 4ba3162f3e Handle pane commands on PaneElement via command registry 2014-09-24 15:28:33 -06:00
Nathan Sobo 2710c06313 WIP: First stab at custom element for panes
Still need to create a SpacePen shim for access via `atom.workspaceView`
property so we’re backward compatible with packages, but it basically
works.
2014-09-24 15:28:33 -06:00
35 arquivos alterados com 1168 adições e 682 exclusões
+1 -1
Ver Arquivo
@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.98.0"
"atom-package-manager": "0.101.0"
}
}
+14 -14
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "atom",
"productName": "Atom",
"version": "0.135.0",
"version": "0.136.0",
"description": "A hackable text editor for the 21st Century.",
"main": "./src/browser/main.js",
"repository": {
@@ -50,14 +50,14 @@
"reactionary-atom-fork": "^1.0.0",
"runas": "1.0.1",
"scandal": "1.0.2",
"scoped-property-store": "^0.9.0",
"scoped-property-store": "^0.11.0",
"scrollbar-style": "^1.0.2",
"season": "^1.0.2",
"semver": "1.1.4",
"serializable": "^1",
"space-pen": "3.4.7",
"space-pen": "3.6.1",
"temp": "0.7.0",
"text-buffer": "^3.2.6",
"text-buffer": "^3.2.8",
"theorist": "^1.0.2",
"underscore-plus": "^1.5.1",
"vm-compatibility-layer": "0.1.0"
@@ -74,11 +74,11 @@
"archive-view": "0.37.0",
"autocomplete": "0.32.0",
"autoflow": "0.18.0",
"autosave": "0.17.0",
"autosave": "0.18.0",
"background-tips": "0.17.0",
"bookmarks": "0.28.0",
"bracket-matcher": "0.61.0",
"command-palette": "0.26.0",
"command-palette": "0.27.0",
"deprecation-cop": "0.10.0",
"dev-live-reload": "0.34.0",
"exception-reporting": "0.20.0",
@@ -98,24 +98,24 @@
"package-generator": "0.31.0",
"release-notes": "0.36.0",
"settings-view": "0.149.0",
"snippets": "0.53.0",
"snippets": "0.55.0",
"spell-check": "0.42.0",
"status-bar": "0.45.0",
"status-bar": "0.46.0",
"styleguide": "0.30.0",
"symbols-view": "0.66.0",
"tabs": "0.54.0",
"timecop": "0.22.0",
"tree-view": "0.127.0",
"tree-view": "0.128.0",
"update-package-dependencies": "0.6.0",
"welcome": "0.18.0",
"whitespace": "0.25.0",
"wrap-guide": "0.22.0",
"language-c": "0.28.0",
"language-coffee-script": "0.35.0",
"language-css": "0.17.0",
"language-gfm": "0.50.0",
"language-css": "0.18.0",
"language-gfm": "0.51.0",
"language-git": "0.9.0",
"language-go": "0.17.0",
"language-go": "0.18.0",
"language-html": "0.26.0",
"language-hyperlink": "0.12.0",
"language-java": "0.11.0",
@@ -129,7 +129,7 @@
"language-php": "0.16.0",
"language-property-list": "0.7.0",
"language-python": "0.19.0",
"language-ruby": "0.38.0",
"language-ruby": "0.39.0",
"language-ruby-on-rails": "0.18.0",
"language-sass": "0.22.0",
"language-shellscript": "0.8.0",
@@ -139,7 +139,7 @@
"language-todo": "0.12.0",
"language-toml": "0.12.0",
"language-xml": "0.22.0",
"language-yaml": "0.17.0"
"language-yaml": "0.18.0"
},
"private": true,
"scripts": {
+1 -1
Ver Arquivo
@@ -20,7 +20,7 @@ describe "install(commandPath, callback)", ->
installDone = false
installError = null
installer.install commandFilePath, false, (error) ->
installer.createSymlink commandFilePath, false, (error) ->
installDone = true
installError = error
+103
Ver Arquivo
@@ -862,3 +862,106 @@ describe "Config", ->
expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true
expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three']
describe "scoped settings", ->
describe ".get(scopeDescriptor, keyPath)", ->
it "returns the property with the most specific scope selector", ->
atom.config.addScopedSettings(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22)
atom.config.addScopedSettings(".source", foo: bar: baz: 11)
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
expect(atom.config.get([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
expect(atom.config.get([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
expect(atom.config.get([".text"], "foo.bar.baz")).toBeUndefined()
it "favors the most recently added properties in the event of a specificity tie", ->
atom.config.addScopedSettings(".source.coffee .string.quoted.single", foo: bar: baz: 42)
atom.config.addScopedSettings(".source.coffee .string.quoted.double", foo: bar: baz: 22)
expect(atom.config.get([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
expect(atom.config.get([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
describe 'when there are global defaults', ->
it 'falls back to the global when there is no scoped property specified', ->
atom.config.setDefaults("foo", hasDefault: 'ok')
expect(atom.config.get([".source.coffee", ".string.quoted.single"], "foo.hasDefault")).toBe 'ok'
describe ".set(scope, keyPath, value)", ->
it "sets the value and overrides the others", ->
atom.config.addScopedSettings(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22)
atom.config.addScopedSettings(".source", foo: bar: baz: 11)
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
expect(atom.config.set(".source.coffee .string.quoted.double.coffee", "foo.bar.baz", 100)).toBe true
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 100
describe ".removeScopedSettingsForName(name)", ->
it "allows properties to be removed by name", ->
disposable1 = atom.config.addScopedSettings("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
disposable2 = atom.config.addScopedSettings("b", ".source .string.quoted.double", foo: bar: baz: 22)
disposable2.dispose()
expect(atom.config.get([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
describe ".observe(scopeDescriptor, keyPath)", ->
it 'calls the supplied callback when the value at the descriptor/keypath changes', ->
atom.config.observe [".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz", changeSpy = jasmine.createSpy()
expect(changeSpy).toHaveBeenCalledWith(undefined)
changeSpy.reset()
atom.config.set("foo.bar.baz", 12)
expect(changeSpy).toHaveBeenCalledWith(12)
changeSpy.reset()
disposable1 = atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22)
expect(changeSpy).toHaveBeenCalledWith(22)
changeSpy.reset()
disposable2 = atom.config.addScopedSettings("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
expect(changeSpy).toHaveBeenCalledWith(42)
changeSpy.reset()
disposable2.dispose()
expect(changeSpy).toHaveBeenCalledWith(22)
changeSpy.reset()
disposable1.dispose()
expect(changeSpy).toHaveBeenCalledWith(12)
changeSpy.reset()
atom.config.set("foo.bar.baz", undefined)
expect(changeSpy).toHaveBeenCalledWith(undefined)
changeSpy.reset()
describe ".onDidChange(scopeDescriptor, keyPath)", ->
it 'calls the supplied callback when the value at the descriptor/keypath changes', ->
keyPath = "foo.bar.baz"
atom.config.onDidChange [".source.coffee", ".string.quoted.double.coffee"], keyPath, changeSpy = jasmine.createSpy()
atom.config.set("foo.bar.baz", 12)
expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12, keyPath})
changeSpy.reset()
disposable1 = atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22)
expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: 22, keyPath})
changeSpy.reset()
disposable2 = atom.config.addScopedSettings("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 42, keyPath})
changeSpy.reset()
disposable2.dispose()
expect(changeSpy).toHaveBeenCalledWith({oldValue: 42, newValue: 22, keyPath})
changeSpy.reset()
disposable1.dispose()
expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 12, keyPath})
changeSpy.reset()
atom.config.set("foo.bar.baz", undefined)
expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined, keyPath})
changeSpy.reset()
+10 -7
Ver Arquivo
@@ -3,7 +3,7 @@ Package = require '../src/package'
describe "PackageManager", ->
beforeEach ->
atom.workspaceView = new WorkspaceView
atom.workspaceView = atom.workspace.getView(atom.workspace).__spacePenView
describe "::loadPackage(name)", ->
it "continues if the package has an invalid package.json", ->
@@ -313,7 +313,7 @@ describe "PackageManager", ->
atom.packages.activatePackage("package-with-scoped-properties")
runs ->
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
expect(atom.config.get ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
describe "converted textmate packages", ->
it "loads the package's grammars", ->
@@ -326,15 +326,18 @@ describe "PackageManager", ->
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
it "loads the translated scoped properties", ->
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
expect(atom.config.get(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
waitsForPromise ->
atom.packages.activatePackage('language-ruby')
runs ->
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
expect(atom.config.get(['.source.ruby'], 'editor.commentStart')).toBe '# '
describe "::deactivatePackage(id)", ->
afterEach ->
atom.packages.unloadPackages()
describe "atom packages", ->
it "calls `deactivate` on the package's main module if activate was successful", ->
pack = null
@@ -436,9 +439,9 @@ describe "PackageManager", ->
atom.packages.activatePackage("package-with-scoped-properties")
runs ->
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
expect(atom.config.get ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
atom.packages.deactivatePackage("package-with-scoped-properties")
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
expect(atom.config.get ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
describe "textmate packages", ->
it "removes the package's grammars", ->
@@ -458,7 +461,7 @@ describe "PackageManager", ->
runs ->
atom.packages.deactivatePackage('language-ruby')
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
expect(atom.config.get(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
describe "::activate()", ->
packageActivator = null
+26
Ver Arquivo
@@ -115,3 +115,29 @@ describe "PaneContainer", ->
pane3.addItems([new Object, new Object])
expect(observed).toEqual container.getPaneItems()
describe "::confirmClose()", ->
[container, pane1, pane2] = []
beforeEach ->
class TestItem
shouldPromptToSave: -> true
getUri: -> 'test'
container = new PaneContainer
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)
saved = container.confirmClose()
expect(saved).toBeTruthy()
expect(atom.confirm).toHaveBeenCalled()
it "returns false if the user cancels saving any modified file", ->
spyOn(atom, "confirm").andReturn(1)
saved = container.confirmClose()
expect(saved).toBeFalsy()
expect(atom.confirm).toHaveBeenCalled()
+7 -29
Ver Arquivo
@@ -1,5 +1,6 @@
path = require 'path'
temp = require 'temp'
PaneContainer = require '../src/pane-container'
PaneContainerView = require '../src/pane-container-view'
PaneView = require '../src/pane-view'
{$, View, $$} = require 'atom'
@@ -18,7 +19,7 @@ describe "PaneContainerView", ->
save: -> @saved = true
isEqual: (other) -> @name is other?.name
container = new PaneContainerView
container = atom.workspace.getView(atom.workspace.paneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(new TestView('1'))
pane2 = pane1.splitRight(new TestView('2'))
@@ -70,32 +71,9 @@ describe "PaneContainerView", ->
for item in pane.getItems()
expect(item.saved).toBeTruthy()
describe ".confirmClose()", ->
it "returns true after modified files are saved", ->
pane1.itemAtIndex(0).shouldPromptToSave = -> true
pane2.itemAtIndex(0).shouldPromptToSave = -> true
spyOn(atom, "confirm").andReturn(0)
saved = container.confirmClose()
runs ->
expect(saved).toBeTruthy()
expect(atom.confirm).toHaveBeenCalled()
it "returns false if the user cancels saving", ->
pane1.itemAtIndex(0).shouldPromptToSave = -> true
pane2.itemAtIndex(0).shouldPromptToSave = -> true
spyOn(atom, "confirm").andReturn(1)
saved = container.confirmClose()
runs ->
expect(saved).toBeFalsy()
expect(atom.confirm).toHaveBeenCalled()
describe "serialization", ->
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist()
@@ -111,14 +89,14 @@ describe "PaneContainerView", ->
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
it "leaves the empty panes intact", ->
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
expect(newContainer.find('.pane-row > .pane-column > .pane').length).toBe 2
describe "if the 'core.destroyEmptyPanes' config option is true", ->
it "removes empty panes on deserialization", ->
atom.config.set('core.destroyEmptyPanes', true)
newContainer = new PaneContainerView(container.model.testSerialization())
newContainer = atom.workspace.getView(container.model.testSerialization()).__spacePenView
expect(newContainer.find('.pane-row, .pane-column')).not.toExist()
expect(newContainer.find('> :contains(1)')).toExist()
@@ -131,7 +109,7 @@ describe "PaneContainerView", ->
item2b = new TestView('2b')
item3a = new TestView('3a')
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(item1a)
container.attachToDom()
@@ -281,7 +259,7 @@ describe "PaneContainerView", ->
# |7|8|9|
# -------
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
pane1 = container.getRoot()
pane1.activateItem(new TestView('1'))
pane4 = pane1.splitDown(new TestView('4'))
+4 -4
Ver Arquivo
@@ -1,4 +1,4 @@
PaneContainerView = require '../src/pane-container-view'
PaneContainer = require '../src/pane-container'
PaneView = require '../src/pane-view'
fs = require 'fs-plus'
{Emitter} = require 'event-kit'
@@ -24,7 +24,7 @@ describe "PaneView", ->
beforeEach ->
deserializerDisposable = atom.deserializers.add(TestView)
container = new PaneContainerView
container = atom.workspace.getView(new PaneContainer).__spacePenView
containerModel = container.model
view1 = new TestView(id: 'view-1', text: 'View 1')
view2 = new TestView(id: 'view-2', text: 'View 2')
@@ -311,13 +311,13 @@ describe "PaneView", ->
container.attachToDom()
pane.focus()
container2 = new PaneContainerView(container.model.testSerialization())
container2 = atom.workspace.getView(container.model.testSerialization()).__spacePenView
pane2 = container2.getRoot()
container2.attachToDom()
expect(pane2).toMatchSelector(':has(:focus)')
$(document.activeElement).blur()
container3 = new PaneContainerView(container.model.testSerialization())
container3 = atom.workspace.getView(container.model.testSerialization()).__spacePenView
pane3 = container3.getRoot()
container3.attachToDom()
expect(pane3).not.toMatchSelector(':has(:focus)')
+11 -5
Ver Arquivo
@@ -29,12 +29,18 @@ atom.keymaps.loadBundledKeymaps()
keyBindingsToRestore = atom.keymaps.getKeyBindings()
commandsToRestore = atom.commands.getSnapshot()
$(window).on 'core:close', -> window.close()
$(window).on 'beforeunload', ->
window.addEventListener 'core:close', -> window.close()
window.addEventListener 'beforeunload', ->
atom.storeWindowDimensions()
atom.saveSync()
$('html,body').css('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
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
if process.platform is 'win32' and process.env.JANKY_SHA1
@@ -61,6 +67,7 @@ isCoreSpec = specDirectory == fs.realpathSync(__dirname)
beforeEach ->
Grim.clearDeprecations() if isCoreSpec
$.fx.off = true
documentTitle = null
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
atom.project = new Project(paths: [projectPath])
atom.workspace = new Workspace()
@@ -75,7 +82,6 @@ beforeEach ->
spyOn(atom, 'saveSync')
atom.syntax.clearGrammarOverrides()
atom.syntax.clearProperties()
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
if specPackageName and packageName is specPackageName
@@ -106,7 +112,7 @@ beforeEach ->
spyOn(TextEditorView.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
TextEditorComponent.performSyncUpdates = true
spyOn(WorkspaceView.prototype, 'setTitle').andCallFake (@title) ->
spyOn(atom, "setRepresentedFilename")
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
spyOn(pathwatcher.File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
@@ -351,7 +357,7 @@ $.fn.enableKeymap = ->
not e.originalEvent.defaultPrevented
$.fn.attachToDom = ->
@appendTo($('#jasmine-content'))
@appendTo($('#jasmine-content')) unless @isOnDom()
$.fn.simulateDomAttachment = ->
$('<html>').append(this)
+3 -3
Ver Arquivo
@@ -63,9 +63,9 @@ describe "Window", ->
beforeUnloadEvent = $.Event(new Event('beforeunload'))
describe "when pane items are are modified", ->
it "prompts user to save and and calls workspaceView.confirmClose", ->
it "prompts user to save and calls atom.workspace.confirmClose", ->
editor = null
spyOn(atom.workspaceView, 'confirmClose').andCallThrough()
spyOn(atom.workspace, 'confirmClose').andCallThrough()
spyOn(atom, "confirm").andReturn(2)
waitsForPromise ->
@@ -74,7 +74,7 @@ describe "Window", ->
runs ->
editor.insertText("I look different, I feel different.")
$(window).trigger(beforeUnloadEvent)
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
expect(atom.workspace.confirmClose).toHaveBeenCalled()
expect(atom.confirm).toHaveBeenCalled()
it "prompts user to save and handler returns true if don't save", ->
+87
Ver Arquivo
@@ -1,3 +1,5 @@
path = require 'path'
temp = require 'temp'
Workspace = require '../src/workspace'
{View} = require '../src/space-pen-extensions'
@@ -369,3 +371,88 @@ describe "Workspace", ->
workspace2 = Workspace.deserialize(state)
expect(jsPackage.loadGrammarsSync.callCount).toBe 1
expect(coffeePackage.loadGrammarsSync.callCount).toBe 1
describe "document.title", ->
describe "when the project has no path", ->
it "sets the title to 'untitled'", ->
atom.project.setPath(undefined)
expect(document.title).toBe 'untitled'
describe "when the project has a path", ->
beforeEach ->
waitsForPromise ->
atom.workspace.open('b')
describe "when there is an active pane item", ->
it "sets the title to the pane item's title plus the project path", ->
item = atom.workspace.getActivePaneItem()
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the title of the active pane item changes", ->
it "updates the window title based on the item's new title", ->
editor = atom.workspace.getActivePaneItem()
editor.buffer.setPath(path.join(temp.dir, 'hi'))
expect(document.title).toBe "#{editor.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the active pane's item changes", ->
it "updates the title to the new item's title plus the project path", ->
atom.workspace.getActivePane().activateNextItem()
item = atom.workspace.getActivePaneItem()
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the last pane item is removed", ->
it "updates the title to contain the project's path", ->
atom.workspace.getActivePane().destroy()
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
expect(document.title).toBe atom.project.getPaths()[0]
describe "when an inactive pane's item changes", ->
it "does not update the title", ->
pane = atom.workspace.getActivePane()
pane.splitRight()
initialTitle = document.title
pane.activateNextItem()
expect(document.title).toBe initialTitle
describe "when the workspace is deserialized", ->
beforeEach ->
waitsForPromise -> atom.workspace.open('a')
it "updates the title to contain the project's path", ->
document.title = null
workspace2 = atom.workspace.testSerialization()
item = atom.workspace.getActivePaneItem()
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
workspace2.destroy()
describe "document edited status", ->
[item1, item2] = []
beforeEach ->
waitsForPromise -> atom.workspace.open('a')
waitsForPromise -> atom.workspace.open('b')
runs ->
[item1, item2] = atom.workspace.getPaneItems()
spyOn(atom, 'setDocumentEdited')
it "calls atom.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)
it "calls atom.setDocumentEdited when the active item's modified status changes", ->
expect(atom.workspace.getActivePaneItem()).toBe item2
item2.insertText('a')
advanceClock(item2.getBuffer().getStoppedChangingDelay())
expect(item2.isModified()).toBe true
expect(atom.setDocumentEdited).toHaveBeenCalledWith(true)
item2.undo()
advanceClock(item2.getBuffer().getStoppedChangingDelay())
expect(item2.isModified()).toBe false
expect(atom.setDocumentEdited).toHaveBeenCalledWith(false)
+2 -52
Ver Arquivo
@@ -49,7 +49,7 @@ describe "WorkspaceView", ->
expect(atom.workspaceView.getEditorViews().length).toBe 2
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPaths()[0]}"
expect(document.title).toBe "untitled - #{atom.project.getPaths()[0]}"
describe "when there are open editors", ->
it "constructs the view with the same panes", ->
@@ -106,7 +106,7 @@ describe "WorkspaceView", ->
expect(editorView3).not.toHaveFocus()
expect(editorView4).not.toHaveFocus()
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPaths()[0]}"
expect(document.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPaths()[0]}"
describe "where there are no open editors", ->
it "constructs the view with no open editors", ->
@@ -141,56 +141,6 @@ describe "WorkspaceView", ->
atom.workspaceView.trigger(event)
expect(commandHandler).toHaveBeenCalled()
describe "window title", ->
describe "when the project has no path", ->
it "sets the title to 'untitled'", ->
atom.project.setPaths([])
expect(atom.workspaceView.title).toBe 'untitled'
describe "when the project has a path", ->
beforeEach ->
waitsForPromise ->
atom.workspace.open('b')
describe "when there is an active pane item", ->
it "sets the title to the pane item's title plus the project path", ->
item = atom.workspace.getActivePaneItem()
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the title of the active pane item changes", ->
it "updates the window title based on the item's new title", ->
editor = atom.workspace.getActivePaneItem()
editor.buffer.setPath(path.join(temp.dir, 'hi'))
expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the active pane's item changes", ->
it "updates the title to the new item's title plus the project path", ->
atom.workspaceView.getActivePaneView().activateNextItem()
item = atom.workspace.getActivePaneItem()
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
describe "when the last pane item is removed", ->
it "updates the title to contain the project's path", ->
atom.workspaceView.getActivePaneView().remove()
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
expect(atom.workspaceView.title).toBe atom.project.getPaths()[0]
describe "when an inactive pane's item changes", ->
it "does not update the title", ->
pane = atom.workspaceView.getActivePaneView()
pane.splitRight()
initialTitle = atom.workspaceView.title
pane.activateNextItem()
expect(atom.workspaceView.title).toBe initialTitle
describe "when the root view is deserialized", ->
it "updates the title to contain the project's path", ->
workspace2 = atom.workspace.testSerialization()
workspaceView2 = workspace2.getView(workspace2).__spacePenView
item = atom.workspace.getActivePaneItem()
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]}"
workspaceView2.remove()
describe "window:toggle-invisibles event", ->
it "shows/hides invisibles in all open and future editors", ->
atom.workspaceView.height(200)
+29 -10
Ver Arquivo
@@ -30,14 +30,41 @@ module.exports =
getInstallDirectory: ->
"/usr/local/bin"
install: (commandPath, askForPrivilege, callback) ->
installShellCommandsInteractively: ->
showErrorDialog = (error) ->
atom.confirm
message: "Failed to install shell commands"
detailedMessage: error.message
resourcePath = atom.getLoadSettings().resourcePath
@installAtomCommand resourcePath, true, (error) =>
if error?
showErrorDialog(error)
else
@installApmCommand resourcePath, true, (error) ->
if error?
showErrorDialog(error)
else
atom.confirm
message: "Commands installed."
detailedMessage: "The shell commands `atom` and `apm` are installed."
installAtomCommand: (resourcePath, askForPrivilege, callback) ->
commandPath = path.join(resourcePath, 'atom.sh')
@createSymlink commandPath, askForPrivilege, callback
installApmCommand: (resourcePath, askForPrivilege, callback) ->
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
@createSymlink commandPath, askForPrivilege, callback
createSymlink: (commandPath, askForPrivilege, callback) ->
return unless process.platform is 'darwin'
commandName = path.basename(commandPath, path.extname(commandPath))
destinationPath = path.join(@getInstallDirectory(), commandName)
fs.readlink destinationPath, (error, realpath) ->
if realpath == commandPath
if realpath is commandPath
callback()
return
@@ -49,11 +76,3 @@ module.exports =
catch error
callback?(error)
installAtomCommand: (resourcePath, askForPrivilege, callback) ->
commandPath = path.join(resourcePath, 'atom.sh')
@install commandPath, askForPrivilege, callback
installApmCommand: (resourcePath, askForPrivilege, callback) ->
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
@install commandPath, askForPrivilege, callback
+16 -2
Ver Arquivo
@@ -1,7 +1,8 @@
path = require 'path'
fs = require 'fs-plus'
# This is loaded by atom.coffee
# This is loaded by atom.coffee. See https://atom.io/docs/api/latest/Config for
# more information about config schemas.
module.exports =
core:
type: 'object'
@@ -38,6 +39,19 @@ module.exports =
editor:
type: 'object'
properties:
# These settings are used in scoped fashion only. No defaults.
commentStart:
type: ['string', 'null']
commentEnd:
type: ['string', 'null']
increaseIndentPattern:
type: ['string', 'null']
decreaseIndentPattern:
type: ['string', 'null']
foldEndPattern:
type: ['string', 'null']
# These can be used as globals or scoped, thus defaults.
fontFamily:
type: 'string'
default: ''
@@ -73,7 +87,7 @@ module.exports =
tabLength:
type: 'integer'
default: 2
enum: [1, 2, 4, 6, 8]
enum: [1, 2, 3, 4, 6, 8]
softWrap:
type: 'boolean'
default: false
+285 -89
Ver Arquivo
@@ -1,13 +1,15 @@
_ = require 'underscore-plus'
fs = require 'fs-plus'
EmitterMixin = require('emissary').Emitter
{Emitter} = require 'event-kit'
{Disposable, Emitter} = require 'event-kit'
CSON = require 'season'
path = require 'path'
async = require 'async'
pathWatcher = require 'pathwatcher'
{deprecate} = require 'grim'
ScopedPropertyStore = require 'scoped-property-store'
# Essential: Used to access all of Atom's configuration details.
#
# An instance of this class is always available as the `atom.config` global.
@@ -219,7 +221,7 @@ pathWatcher = require 'pathwatcher'
#
# All types support an `enum` key. The enum key lets you specify all values
# that the config setting can possibly be. `enum` _must_ be an array of values
# of your specified type.
# of your specified type. Schema:
#
# ```coffee
# config:
@@ -229,6 +231,8 @@ pathWatcher = require 'pathwatcher'
# enum: [2, 4, 6, 8]
# ```
#
# Usage:
#
# ```coffee
# atom.config.set('my-package.someSetting', '2')
# atom.config.get('my-package.someSetting') # -> 2
@@ -307,6 +311,7 @@ class Config
properties: {}
@defaultSettings = {}
@settings = {}
@scopedSettingsStore = new ScopedPropertyStore
@configFileHasErrors = false
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= path.join(@configDirPath, 'config.cson')
@@ -319,29 +324,57 @@ class Config
# than {::onDidChange} in that it will immediately call your callback with the
# current value of the config entry.
#
# ### Examples
#
# You might want to be notified when the themes change. We'll watch
# `core.themes` for changes
#
# ```coffee
# atom.config.observe 'core.themes', (value) ->
# # do stuff with value
# ```
#
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
# the root of the syntax tree to a token. Get one by calling
# {TextEditor::scopesAtCursor}. See {::get} for examples.
# * `keyPath` {String} name of the key to observe
# * `callback` {Function} to call when the value of the key changes.
# * `value` the new value of the key
#
# Returns a {Disposable} with the following keys on which you can call
# `.dispose()` to unsubscribe.
observe: (keyPath, options={}, callback) ->
if _.isFunction(options)
callback = options
options = {}
else
observe: (scopeDescriptor, keyPath, options, callback) ->
args = Array::slice.call(arguments)
if args.length is 2
# observe(keyPath, callback)
[keyPath, callback, scopeDescriptor, options] = args
else if args.length is 3 and Array.isArray(scopeDescriptor)
# observe(scopeDescriptor, keyPath, callback)
[scopeDescriptor, keyPath, callback, options] = args
else if args.length is 3 and _.isString(scopeDescriptor) and _.isObject(keyPath)
# observe(keyPath, options, callback) # Deprecated!
[keyPath, options, callback, scopeDescriptor] = args
message = ""
message = "`callNow` was set to false. Use ::onDidChange instead. Note that ::onDidChange calls back with different arguments." if options.callNow == false
deprecate "Config::observe no longer supports options. #{message}"
deprecate "Config::observe no longer supports options; see https://atom.io/docs/api/latest/Config. #{message}"
else
console.error 'An unsupported form of Config::observe is being used. See https://atom.io/docs/api/latest/Config for details'
return
callback(_.clone(@get(keyPath))) unless options.callNow == false
@emitter.on 'did-change', (event) ->
callback(event.newValue) if keyPath? and keyPath.indexOf(event?.keyPath) is 0
if scopeDescriptor?
@observeScopedKeyPath(scopeDescriptor, keyPath, callback)
else
@observeKeyPath(keyPath, options ? {}, callback)
# Essential: Add a listener for changes to a given key path. If `keyPath` is
# not specified, your callback will be called on changes to any key.
#
# * `keyPath` (optional) {String} name of the key to observe
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
# the root of the syntax tree to a token. Get one by calling
# {TextEditor::scopesAtCursor}. See {::get} for examples.
# * `keyPath` (optional) {String} name of the key to observe. Must be
# specified if `scopeDescriptor` is specified.
# * `callback` {Function} to call when the value of the key changes.
# * `event` {Object}
# * `newValue` the new value of the key
@@ -350,13 +383,17 @@ class Config
#
# Returns a {Disposable} with the following keys on which you can call
# `.dispose()` to unsubscribe.
onDidChange: (keyPath, callback) ->
onDidChange: (scopeDescriptor, keyPath, callback) ->
args = Array::slice.call(arguments)
if arguments.length is 1
callback = keyPath
keyPath = undefined
[callback, scopeDescriptor, keyPath] = args
else if arguments.length is 2
[keyPath, callback, scopeDescriptor] = args
@emitter.on 'did-change', (event) ->
callback(event) if not keyPath? or (keyPath? and keyPath.indexOf(event?.keyPath) is 0)
if scopeDescriptor?
@onDidChangeScopedKeyPath(scopeDescriptor, keyPath, callback)
else
@onDidChangeKeyPath(keyPath, callback)
###
Section: Managing Settings
@@ -364,29 +401,84 @@ class Config
# Essential: Retrieves the setting for the given key.
#
# ### Examples
#
# You might want to know what themes are enabled, so check `core.themes`
#
# ```coffee
# atom.config.get('core.themes')
# ```
#
# With scope descriptors you can get settings within a specific editor
# scope. For example, you might want to know `editor.tabLength` for ruby
# files.
#
# ```coffee
# atom.config.get(['source.ruby'], 'editor.tabLength') # => 2
# ```
#
# This setting in ruby files might be different than the global tabLength setting
#
# ```coffee
# atom.config.get('editor.tabLength') # => 4
# atom.config.get(['source.ruby'], 'editor.tabLength') # => 2
# ```
#
# Additionally, you can get the setting at the specific cursor position.
#
# ```coffee
# scopeDescriptor = @editor.scopesAtCursor()
# atom.config.get(scopeDescriptor, 'editor.tabLength') # => 2
# ```
#
# * `scopeDescriptor` (optional) {Array} of {String}s describing a path from
# the root of the syntax tree to a token. Get one by calling
# {TextEditor::scopesAtCursor}
# * `keyPath` The {String} name of the key to retrieve.
#
# Returns the value from Atom's default settings, the user's configuration
# file in the type specified by the configuration schema.
get: (keyPath) ->
value = _.valueForKeyPath(@settings, keyPath)
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
get: (scopeDescriptor, keyPath) ->
if arguments.length == 1
keyPath = scopeDescriptor
scopeDescriptor = undefined
if value?
value = _.deepClone(value)
valueIsObject = _.isObject(value) and not _.isArray(value)
defaultValueIsObject = _.isObject(defaultValue) and not _.isArray(defaultValue)
if valueIsObject and defaultValueIsObject
_.defaults(value, defaultValue)
else
value = _.deepClone(defaultValue)
if scopeDescriptor?
value = @getRawScopedValue(scopeDescriptor, keyPath)
return value if value?
value
@getRawValue(keyPath)
# Essential: Sets the value for a configuration setting.
#
# This value is stored in Atom's internal configuration file.
#
# ### Examples
#
# You might want to change the themes programmatically:
#
# ```coffee
# atom.config.set('core.themes', ['atom-light-ui', 'atom-light-syntax'])
# ```
#
# You can also set scoped settings. For example, you might want change the
# `editor.tabLength` only for ruby files.
#
# ```coffee
# atom.config.get('editor.tabLength') # => 4
# atom.config.get(['source.ruby'], 'editor.tabLength') # => 4
# atom.config.get(['source.js'], 'editor.tabLength') # => 4
#
# # Set ruby to 2
# atom.config.set('source.ruby', 'editor.tabLength', 2) # => true
#
# # Notice it's only set to 2 in the case of ruby
# atom.config.get('editor.tabLength') # => 4
# atom.config.get(['source.ruby'], 'editor.tabLength') # => 2
# atom.config.get(['source.js'], 'editor.tabLength') # => 4
# ```
#
# * `scope` (optional) {String}. eg. '.source.ruby'
# * `keyPath` The {String} name of the key.
# * `value` The value of the setting. Passing `undefined` will revert the
# setting to the default value.
@@ -394,18 +486,27 @@ class Config
# Returns a {Boolean}
# * `true` if the value was set.
# * `false` if the value was not able to be coerced to the type specified in the setting's schema.
set: (keyPath, value) ->
set: (scope, keyPath, value) ->
if arguments.length < 3
value = keyPath
keyPath = scope
scope = undefined
unless value == undefined
try
value = @makeValueConformToSchema(keyPath, value)
catch e
return false
@setRawValue(keyPath, value)
if scope?
@setRawScopedValue(scope, keyPath, value)
else
@setRawValue(keyPath, value)
@save() unless @configFileHasErrors
true
# Extended: Restore the key path to its default value.
# Extended: Restore the global setting at `keyPath` to its default value.
#
# * `keyPath` The {String} name of the key.
#
@@ -414,7 +515,7 @@ class Config
@set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath))
@get(keyPath)
# Extended: Get the default value of the key path. _Please note_ that in most
# Extended: Get the global default value of the key path. _Please note_ that in most
# cases calling this is not necessary! {::get} returns the default value when
# a custom value is not specified.
#
@@ -425,7 +526,7 @@ class Config
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
_.deepClone(defaultValue)
# Extended: Is the key path value its default value?
# Extended: Is the value at `keyPath` its default value?
#
# * `keyPath` The {String} name of the key.
#
@@ -434,7 +535,7 @@ class Config
isDefault: (keyPath) ->
not _.valueForKeyPath(@settings, keyPath)?
# Extended: Retrieve the schema for a specific key path. The shema will tell
# Extended: Retrieve the schema for a specific key path. The schema will tell
# you what type the keyPath expects, and other metadata about the config
# option.
#
@@ -450,7 +551,8 @@ class Config
schema = schema.properties[key]
schema
# Extended: Returns a new {Object} containing all of settings and defaults.
# Extended: Returns a new {Object} containing all of the global settings and
# defaults. This does not include scoped settings.
getSettings: ->
_.deepExtend(@settings, @defaultSettings)
@@ -484,7 +586,7 @@ class Config
deprecate 'Config::unobserve no longer does anything. Call `.dispose()` on the object returned by Config::observe instead.'
###
Section: Private
Section: Internal methods used by core
###
pushAtKeyPath: (keyPath, value) ->
@@ -505,6 +607,34 @@ class Config
@set(keyPath, arrayValue)
result
setSchema: (keyPath, schema) ->
unless isPlainObject(schema)
throw new Error("Error loading schema for #{keyPath}: schemas can only be objects!")
unless typeof schema.type?
throw new Error("Error loading schema for #{keyPath}: schema objects must have a type attribute")
rootSchema = @schema
if keyPath
for key in keyPath.split('.')
rootSchema.type = 'object'
rootSchema.properties ?= {}
properties = rootSchema.properties
properties[key] ?= {}
rootSchema = properties[key]
_.extend rootSchema, schema
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
load: ->
@initializeConfigDirectory()
@loadUserConfig()
@observeUserConfig()
###
Section: Private methods managing the user's config file
###
initializeConfigDirectory: (done) ->
return if fs.existsSync(@configDirPath)
@@ -521,11 +651,6 @@ class Config
queue.push({sourcePath, destinationPath})
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
load: ->
@initializeConfigDirectory()
@loadUserConfig()
@observeUserConfig()
loadUserConfig: ->
unless fs.existsSync(@configFilePath)
fs.makeTreeSync(path.dirname(@configFilePath))
@@ -555,33 +680,9 @@ class Config
save: ->
CSON.writeFileSync(@configFilePath, @settings)
setRawValue: (keyPath, value) ->
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
value = undefined if _.isEqual(defaultValue, value)
oldValue = _.clone(@get(keyPath))
_.setValueForKeyPath(@settings, keyPath, value)
newValue = @get(keyPath)
@emitter.emit 'did-change', {oldValue, newValue, keyPath} unless _.isEqual(newValue, oldValue)
setRawDefault: (keyPath, value) ->
oldValue = _.clone(@get(keyPath))
_.setValueForKeyPath(@defaultSettings, keyPath, value)
newValue = @get(keyPath)
@emitter.emit 'did-change', {oldValue, newValue, keyPath} unless _.isEqual(newValue, oldValue)
setRecursive: (keyPath, value) ->
if isPlainObject(value)
keys = if keyPath? then keyPath.split('.') else []
for key, childValue of value
continue unless value.hasOwnProperty(key)
@setRecursive(keys.concat([key]).join('.'), childValue)
else
try
value = @makeValueConformToSchema(keyPath, value)
@setRawValue(keyPath, value)
catch e
console.warn("'#{keyPath}' could not be set. Attempted value: #{JSON.stringify(value)}; Schema: #{JSON.stringify(@getSchema(keyPath))}")
###
Section: Private methods managing global settings
###
setAll: (newSettings) ->
unless isPlainObject(newSettings)
@@ -602,6 +703,55 @@ class Config
@setRecursive(null, newSettings)
unsetUnspecifiedValues(null, @settings)
setRecursive: (keyPath, value) ->
if isPlainObject(value)
keys = if keyPath? then keyPath.split('.') else []
for key, childValue of value
continue unless value.hasOwnProperty(key)
@setRecursive(keys.concat([key]).join('.'), childValue)
else
try
value = @makeValueConformToSchema(keyPath, value)
@setRawValue(keyPath, value)
catch e
console.warn("'#{keyPath}' could not be set. Attempted value: #{JSON.stringify(value)}; Schema: #{JSON.stringify(@getSchema(keyPath))}")
getRawValue: (keyPath) ->
value = _.valueForKeyPath(@settings, keyPath)
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
if value?
value = _.deepClone(value)
_.defaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue)
else
value = _.deepClone(defaultValue)
value
setRawValue: (keyPath, value) ->
defaultValue = _.valueForKeyPath(@defaultSettings, keyPath)
value = undefined if _.isEqual(defaultValue, value)
oldValue = _.clone(@get(keyPath))
_.setValueForKeyPath(@settings, keyPath, value)
newValue = @get(keyPath)
@emitter.emit 'did-change', {oldValue, newValue, keyPath} unless _.isEqual(newValue, oldValue)
observeKeyPath: (keyPath, options, callback) ->
callback(_.clone(@get(keyPath))) unless options.callNow == false
@emitter.on 'did-change', (event) ->
callback(event.newValue) if keyPath? and keyPath.indexOf(event?.keyPath) is 0
onDidChangeKeyPath: (keyPath, callback) ->
@emitter.on 'did-change', (event) ->
callback(event) if not keyPath? or (keyPath? and keyPath.indexOf(event?.keyPath) is 0)
setRawDefault: (keyPath, value) ->
oldValue = _.clone(@get(keyPath))
_.setValueForKeyPath(@defaultSettings, keyPath, value)
newValue = @get(keyPath)
@emitter.emit 'did-change', {oldValue, newValue, keyPath} unless _.isEqual(newValue, oldValue)
setDefaults: (keyPath, defaults) ->
if defaults? and isPlainObject(defaults)
keys = if keyPath? then keyPath.split('.') else []
@@ -615,25 +765,6 @@ class Config
catch e
console.warn("'#{keyPath}' could not set the default. Attempted default: #{JSON.stringify(defaults)}; Schema: #{JSON.stringify(@getSchema(keyPath))}")
setSchema: (keyPath, schema) ->
unless isPlainObject(schema)
throw new Error("Error loading schema for #{keyPath}: schemas can only be objects!")
unless typeof schema.type?
throw new Error("Error loading schema for #{keyPath}: schema objects must have a type attribute")
rootSchema = @schema
if keyPath
for key in keyPath.split('.')
rootSchema.type = 'object'
rootSchema.properties ?= {}
properties = rootSchema.properties
properties[key] ?= {}
rootSchema = properties[key]
_.extend rootSchema, schema
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
extractDefaultsFromSchema: (schema) ->
if schema.default?
schema.default
@@ -647,6 +778,71 @@ class Config
value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
value
###
Section: Private Scoped Settings
###
addScopedSettings: (name, selector, value) ->
if arguments.length < 3
value = selector
selector = name
name = null
settingsBySelector = {}
settingsBySelector[selector] = value
disposable = @scopedSettingsStore.addProperties(name, settingsBySelector)
@emitter.emit 'did-change'
new Disposable =>
disposable.dispose()
@emitter.emit 'did-change'
setRawScopedValue: (selector, keyPath, value) ->
if keyPath?
newValue = {}
_.setValueForKeyPath(newValue, keyPath, value)
value = newValue
@addScopedSettings(null, selector, value)
getRawScopedValue: (scopeDescriptor, keyPath) ->
scopeChain = scopeDescriptor
.map (scope) ->
scope = ".#{scope}" unless scope[0] is '.'
scope
.join(' ')
@scopedSettingsStore.getPropertyValue(scopeChain, keyPath)
observeScopedKeyPath: (scopeDescriptor, keyPath, callback) ->
oldValue = @get(scopeDescriptor, keyPath)
callback(oldValue)
didChange = =>
newValue = @get(scopeDescriptor, keyPath)
callback(newValue) unless _.isEqual(oldValue, newValue)
oldValue = newValue
@emitter.on 'did-change', didChange
onDidChangeScopedKeyPath: (scopeDescriptor, keyPath, callback) ->
oldValue = @get(scopeDescriptor, keyPath)
didChange = =>
newValue = @get(scopeDescriptor, keyPath)
callback({oldValue, newValue, keyPath}) unless _.isEqual(oldValue, newValue)
oldValue = newValue
@emitter.on 'did-change', didChange
# TODO: figure out how to change / remove this. The return value is awkward.
# * language mode uses it for one thing.
# * autocomplete uses it for editor.completions
settingsForScopeDescriptor: (scopeDescriptor, keyPath) ->
scopeChain = scopeDescriptor
.map (scope) ->
scope = ".#{scope}" unless scope[0] is '.'
scope
.join(' ')
@scopedSettingsStore.getProperties(scopeChain, keyPath)
# Base schema enforcers. These will coerce raw input into the specified type,
# and will throw an error when the value cannot be coerced. Throwing the error
# will indicate that the value should not be set.
@@ -692,7 +888,7 @@ Config.addSchemaEnforcers
'null':
# null sort of isnt supported. It will just unset in this case
coerce: (keyPath, value, schema) ->
throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be null") unless value == null
throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be null") unless value in [undefined, null]
value
'object':
+4 -4
Ver Arquivo
@@ -30,11 +30,11 @@ class LanguageMode
# Returns an {Array} of the commented {Ranges}.
toggleLineCommentsForBufferRows: (start, end) ->
scopes = @editor.scopesForBufferPosition([start, 0])
properties = atom.syntax.propertiesForScope(scopes, "editor.commentStart")[0]
properties = atom.config.settingsForScopeDescriptor(scopes, 'editor.commentStart')[0]
return unless properties
commentStartString = _.valueForKeyPath(properties, "editor.commentStart")
commentEndString = _.valueForKeyPath(properties, "editor.commentEnd")
commentStartString = _.valueForKeyPath(properties, 'editor.commentStart')
commentEndString = _.valueForKeyPath(properties, 'editor.commentEnd')
return unless commentStartString
@@ -312,7 +312,7 @@ class LanguageMode
@editor.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
getRegexForProperty: (scopes, property) ->
if pattern = atom.syntax.getProperty(scopes, property)
if pattern = atom.config.get(scopes, property)
new OnigRegExp(pattern)
increaseIndentRegexForScopes: (scopes) ->
+2 -2
Ver Arquivo
@@ -133,10 +133,10 @@ class MenuManager
element?.classList.toString().split(' ') ? []
sortPackagesMenu: ->
packagesMenu = @template.find ({label}) => MenuHelpers.normalizeLabel(label) is 'Packages'
packagesMenu = @template.find ({label}) -> MenuHelpers.normalizeLabel(label) is 'Packages'
return unless packagesMenu?.submenu?
packagesMenu.submenu.sort (item1, item2) =>
packagesMenu.submenu.sort (item1, item2) ->
if item1.label and item2.label
MenuHelpers.normalizeLabel(item1.label).localeCompare(MenuHelpers.normalizeLabel(item2.label))
else
+44
Ver Arquivo
@@ -0,0 +1,44 @@
{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'
class PaneAxisElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
detachedCallback: ->
@subscriptions.dispose()
setModel: (@model) ->
@subscriptions.add @model.onDidAddChild(@childAdded.bind(this))
@subscriptions.add @model.onDidRemoveChild(@childRemoved.bind(this))
@subscriptions.add @model.onDidReplaceChild(@childReplaced.bind(this))
@childAdded({child, index}) for child, index in @model.getChildren()
switch @model.getOrientation()
when 'horizontal'
@classList.add('pane-row')
when 'vertical'
@classList.add('pane-column')
childAdded: ({child, index}) ->
view = @model.getView(child)
@insertBefore(view, @children[index])
callAttachHooks(view) # for backward compatibility with SpacePen views
childRemoved: ({child}) ->
view = @model.getView(child)
view.remove()
childReplaced: ({index, oldChild, newChild}) ->
focusedElement = document.activeElement if @hasFocus()
@childRemoved({child: oldChild, index})
@childAdded({child: newChild, index})
focusedElement?.focus() if document.activeElement is document.body
hasFocus: ->
this is document.activeElement or @contains(document.activeElement)
module.exports = PaneAxisElement = document.registerElement 'atom-pane-axis',
prototype: PaneAxisElement.prototype
extends: 'div'
-37
Ver Arquivo
@@ -1,37 +0,0 @@
{CompositeDisposable} = require 'event-kit'
{View} = require './space-pen-extensions'
PaneView = null
module.exports =
class PaneAxisView extends View
initialize: (@model) ->
@subscriptions = new CompositeDisposable
@onChildAdded({child, index}) for child, index in @model.getChildren()
@subscriptions.add @model.onDidAddChild(@onChildAdded)
@subscriptions.add @model.onDidRemoveChild(@onChildRemoved)
@subscriptions.add @model.onDidReplaceChild(@onChildReplaced)
afterAttach: ->
@container = @closest('.panes').view()
onChildReplaced: ({index, oldChild, newChild}) =>
focusedElement = document.activeElement if @hasFocus()
@onChildRemoved({child: oldChild, index})
@onChildAdded({child: newChild, index})
focusedElement?.focus() if document.activeElement is document.body
onChildAdded: ({child, index}) =>
view = @model.getView(child).__spacePenView
@insertAt(index, view)
onChildRemoved: ({child}) =>
view = @model.getView(child).__spacePenView
view.detach()
PaneView ?= require './pane-view'
if view instanceof PaneView and view.model.isDestroyed()
@container?.trigger 'pane:removed', [view]
beforeRemove: ->
@subscriptions.dispose()
+1 -8
Ver Arquivo
@@ -3,9 +3,6 @@
{flatten} = require 'underscore-plus'
Serializable = require 'serializable'
PaneRowView = null
PaneColumnView = null
module.exports =
class PaneAxis extends Model
atom.deserializers.add(this)
@@ -40,11 +37,7 @@ class PaneAxis extends Model
setContainer: (@container) -> @container
getViewClass: ->
if @orientation is 'vertical'
PaneColumnView ?= require './pane-column-view'
else
PaneRowView ?= require './pane-row-view'
getOrientation: -> @orientation
getView: (object) ->
@container.getView(object)
-12
Ver Arquivo
@@ -1,12 +0,0 @@
{$} = require './space-pen-extensions'
_ = require 'underscore-plus'
PaneAxisView = require './pane-axis-view'
module.exports =
class PaneColumnView extends PaneAxisView
@content: ->
@div class: 'pane-column'
className: ->
"PaneColumn"
+80
Ver Arquivo
@@ -0,0 +1,80 @@
{CompositeDisposable} = require 'event-kit'
{callAttachHooks} = require './space-pen-extensions'
PaneContainerView = null
_ = require 'underscore-plus'
module.exports =
class PaneContainerElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
@classList.add 'panes'
PaneContainerView ?= require './pane-container-view'
@__spacePenView = new PaneContainerView(this)
setModel: (@model) ->
@subscriptions.add @model.observeRoot(@rootChanged.bind(this))
@__spacePenView.setModel(@model)
rootChanged: (root) ->
focusedElement = document.activeElement if @hasFocus()
@firstChild?.remove()
if root?
view = @model.getView(root)
@appendChild(view)
callAttachHooks(view)
focusedElement?.focus()
hasFocus: ->
this is document.activeElement or @contains(document.activeElement)
focusPaneViewAbove: ->
@nearestPaneInDirection('above')?.focus()
focusPaneViewBelow: ->
@nearestPaneInDirection('below')?.focus()
focusPaneViewOnLeft: ->
@nearestPaneInDirection('left')?.focus()
focusPaneViewOnRight: ->
@nearestPaneInDirection('right')?.focus()
nearestPaneInDirection: (direction) ->
distance = (pointA, pointB) ->
x = pointB.x - pointA.x
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
paneView = @model.getView(@model.getActivePane())
box = @boundingBoxForPaneView(paneView)
paneViews = _.toArray(@querySelectorAll('.pane'))
.filter (otherPaneView) =>
otherBox = @boundingBoxForPaneView(otherPaneView)
switch direction
when 'left' then otherBox.right.x <= box.left.x
when 'right' then otherBox.left.x >= box.right.x
when 'above' then otherBox.bottom.y <= box.top.y
when 'below' then otherBox.top.y >= box.bottom.y
.sort (paneViewA, paneViewB) =>
boxA = @boundingBoxForPaneView(paneViewA)
boxB = @boundingBoxForPaneView(paneViewB)
switch direction
when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right)
when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left)
when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom)
when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top)
paneViews[0]
boundingBoxForPaneView: (paneView) ->
boundingBox = paneView.getBoundingClientRect()
left: {x: boundingBox.left, y: boundingBox.top}
right: {x: boundingBox.right, y: boundingBox.top}
top: {x: boundingBox.left, y: boundingBox.top}
bottom: {x: boundingBox.left, y: boundingBox.bottom}
module.exports = PaneContainerElement = document.registerElement 'atom-pane-container',
prototype: PaneContainerElement.prototype
extends: 'div'
+11 -78
Ver Arquivo
@@ -1,7 +1,7 @@
{deprecate} = require 'grim'
Delegator = require 'delegato'
{CompositeDisposable} = require 'event-kit'
{$, View} = require './space-pen-extensions'
{$, View, callAttachHooks} = require './space-pen-extensions'
PaneView = require './pane-view'
PaneContainer = require './pane-container'
@@ -15,50 +15,22 @@ class PaneContainerView extends View
@content: ->
@div class: 'panes'
initialize: (params) ->
constructor: (@element) ->
super
@subscriptions = new CompositeDisposable
if params instanceof PaneContainer
@model = params
else
@model = new PaneContainer({root: params?.root?.model})
@subscriptions.add @model.observeRoot(@onRootChanged)
setModel: (@model) ->
@subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged)
getRoot: ->
@children().first().view()
onRootChanged: (root) =>
focusedElement = document.activeElement if @hasFocus()
oldRoot = @getRoot()
if oldRoot instanceof PaneView and oldRoot.model.isDestroyed()
@trigger 'pane:removed', [oldRoot]
oldRoot?.detach()
if root?
view = @model.getView(root).__spacePenView
@append(view)
focusedElement?.focus()
else
atom.workspaceView?.focus() if focusedElement?
view = @model.getView(@model.getRoot())
view.__spacePenView ? view
onActivePaneItemChanged: (activeItem) =>
@trigger 'pane-container:active-pane-item-changed', [activeItem]
removeChild: (child) ->
throw new Error("Removing non-existant child") unless @getRoot() is child
@setRoot(null)
@trigger 'pane:removed', [child] if child instanceof PaneView
confirmClose: ->
saved = true
for paneView in @getPaneViews()
for item in paneView.getItems()
if not paneView.promptToSaveItem(item)
saved = false
break
saved
@model.confirmClose()
getPaneViews: ->
@find('.pane').views()
@@ -101,56 +73,17 @@ class PaneContainerView extends View
@model.activatePreviousPane()
focusPaneViewAbove: ->
@nearestPaneInDirection('above')?.focus()
@element.focusPaneViewAbove()
focusPaneViewBelow: ->
@nearestPaneInDirection('below')?.focus()
@element.focusPaneViewBelow()
focusPaneViewOnLeft: ->
@nearestPaneInDirection('left')?.focus()
@element.focusPaneViewOnLeft()
focusPaneViewOnRight: ->
@nearestPaneInDirection('right')?.focus()
@element.focusPaneViewOnRight()
nearestPaneInDirection: (direction) ->
distance = (pointA, pointB) ->
x = pointB.x - pointA.x
y = pointB.y - pointA.y
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
paneView = @getActivePaneView()
box = @boundingBoxForPaneView(paneView)
paneViews = @getPaneViews()
.filter (otherPaneView) =>
otherBox = @boundingBoxForPaneView(otherPaneView)
switch direction
when 'left' then otherBox.right.x <= box.left.x
when 'right' then otherBox.left.x >= box.right.x
when 'above' then otherBox.bottom.y <= box.top.y
when 'below' then otherBox.top.y >= box.bottom.y
.sort (paneViewA, paneViewB) =>
boxA = @boundingBoxForPaneView(paneViewA)
boxB = @boundingBoxForPaneView(paneViewB)
switch direction
when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right)
when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left)
when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom)
when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top)
paneViews[0]
boundingBoxForPaneView: (paneView) ->
boundingBox = paneView[0].getBoundingClientRect()
left: {x: boundingBox.left, y: boundingBox.top}
right: {x: boundingBox.right, y: boundingBox.top}
top: {x: boundingBox.left, y: boundingBox.top}
bottom: {x: boundingBox.left, y: boundingBox.bottom}
# Deprecated
getPanes: ->
deprecate("Use PaneContainerView::getPaneViews() instead")
@getPaneViews()
beforeRemove: ->
@subscriptions.dispose()
+30 -4
Ver Arquivo
@@ -3,9 +3,12 @@
{Emitter, CompositeDisposable} = require 'event-kit'
Serializable = require 'serializable'
Pane = require './pane'
PaneElement = require './pane-element'
PaneContainerElement = require './pane-container-element'
PaneAxisElement = require './pane-axis-element'
PaneAxis = require './pane-axis'
ViewRegistry = require './view-registry'
ItemRegistry = require './item-registry'
PaneContainerView = null
module.exports =
class PaneContainer extends Model
@@ -30,8 +33,10 @@ class PaneContainer extends Model
@emitter = new Emitter
@subscriptions = new CompositeDisposable
@viewRegistry = params?.viewRegistry ? new ViewRegistry
@itemRegistry = new ItemRegistry
@viewRegistry = params?.viewRegistry ? new ViewRegistry
@registerViewProviders()
@setRoot(params?.root ? new Pane)
@destroyEmptyPanes() if params?.destroyEmptyPanes
@@ -48,8 +53,18 @@ class PaneContainer extends Model
root: @root?.serialize()
activePaneId: @activePane.id
getViewClass: ->
PaneContainerView ?= require './pane-container-view'
registerViewProviders: ->
@viewRegistry.addViewProvider
modelConstructor: PaneContainer
viewConstructor: PaneContainerElement
@viewRegistry.addViewProvider
modelConstructor: PaneAxis
viewConstructor: PaneAxisElement
@viewRegistry.addViewProvider
modelConstructor: Pane
viewConstructor: PaneElement
getView: (object) ->
@viewRegistry.getView(object)
@@ -129,6 +144,17 @@ class PaneContainer extends Model
saveAll: ->
pane.saveItems() for pane in @getPanes()
confirmClose: ->
allSaved = true
for pane in @getPanes()
for item in pane.getItems()
unless pane.promptToSaveItem(item)
allSaved = false
break
allSaved
activateNextPane: ->
panes = @getPanes()
if panes.length > 1
+113
Ver Arquivo
@@ -0,0 +1,113 @@
{CompositeDisposable} = require 'event-kit'
{$} = require './space-pen-extensions'
PaneView = require './pane-view'
class PaneElement extends HTMLElement
attached: false
createdCallback: ->
@attached = false
@subscriptions = new CompositeDisposable
@initializeContent()
@subscribeToDOMEvents()
@createSpacePenShim()
attachedCallback: ->
@attached = true
@focus() if @model.isFocused()
detachedCallback: ->
@attached = false
initializeContent: ->
@setAttribute 'class', 'pane'
@setAttribute 'tabindex', -1
@appendChild @itemViews = document.createElement('div')
@itemViews.setAttribute 'class', 'item-views'
subscribeToDOMEvents: ->
@addEventListener 'focusin', => @model.focus()
@addEventListener 'focusout', => @model.blur()
@addEventListener 'focus', => @getActiveView()?.focus()
createSpacePenShim: ->
@__spacePenView = new PaneView(this)
addCommands = (handlersByName) =>
for name, handler of handlersByName
do (handler) =>
@__spacePenView.command name, => handler.apply(this, arguments)
addCommands(
'pane:save-items': -> @getModel().saveItems()
'pane:show-next-item': -> @getModel().activateNextItem()
'pane:show-previous-item': -> @getModel().activatePreviousItem()
'pane:show-item-1': -> @getModel().activateItemAtIndex(0)
'pane:show-item-2': -> @getModel().activateItemAtIndex(1)
'pane:show-item-3': -> @getModel().activateItemAtIndex(2)
'pane:show-item-4': -> @getModel().activateItemAtIndex(3)
'pane:show-item-5': -> @getModel().activateItemAtIndex(4)
'pane:show-item-6': -> @getModel().activateItemAtIndex(5)
'pane:show-item-7': -> @getModel().activateItemAtIndex(6)
'pane:show-item-8': -> @getModel().activateItemAtIndex(7)
'pane:show-item-9': -> @getModel().activateItemAtIndex(8)
'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().destroy()
'pane:close-other-items': -> @getModel().destroyInactiveItems()
)
getModel: -> @model
setModel: (@model) ->
@subscriptions.add @model.onDidActivate(@activated.bind(this))
@subscriptions.add @model.observeActive(@activeStatusChanged.bind(this))
@subscriptions.add @model.observeActiveItem(@activeItemChanged.bind(this))
@subscriptions.add @model.onDidRemoveItem(@itemRemoved.bind(this))
@subscriptions.add @model.onDidDestroy(@paneDestroyed.bind(this))
@__spacePenView.setModel(@model)
activated: ->
@focus() unless @hasFocus()
activeStatusChanged: (active) ->
if active
@classList.add('active')
else
@classList.remove('active')
activeItemChanged: (item) ->
return unless item?
$itemViews = $(@itemViews)
view = @model.getView(item).__spacePenView
otherView.hide() for otherView in $itemViews.children().not(view).views()
$itemViews.append(view) unless view.parent().is($itemViews)
view.show() if @attached
view.focus() if @hasFocus()
itemRemoved: ({item, index, destroyed}) ->
if item instanceof $
viewToRemove = item
else
viewToRemove = @model.getView(item).__spacePenView
if viewToRemove?
if destroyed
viewToRemove.remove()
else
viewToRemove.detach()
paneDestroyed: ->
@subscriptions.dispose()
getActiveView: -> @model.getView(@model.getActiveItem())
hasFocus: ->
this is document.activeElement or @contains(document.activeElement)
module.exports = PaneElement = document.registerElement 'atom-pane',
prototype: PaneElement.prototype
extends: 'div'
-11
Ver Arquivo
@@ -1,11 +0,0 @@
{$} = require './space-pen-extensions'
_ = require 'underscore-plus'
PaneAxisView = require './pane-axis-view'
module.exports =
class PaneRowView extends PaneAxisView
@content: ->
@div class: 'pane-row'
className: ->
"PaneRow"
+20 -76
Ver Arquivo
@@ -17,12 +17,6 @@ class PaneView extends View
Delegator.includeInto(this)
PropertyAccessors.includeInto(this)
@version: 1
@content: (wrappedView) ->
@div class: 'pane', tabindex: -1, =>
@div class: 'item-views', outlet: 'itemViews'
@delegatesProperties 'items', 'activeItem', toProperty: 'model'
@delegatesMethods 'getItems', 'activateNextItem', 'activatePreviousItem', 'getActiveItemIndex',
'activateItemAtIndex', 'activateItem', 'addItem', 'itemAtIndex', 'moveItem', 'moveItemToPane',
@@ -32,49 +26,33 @@ class PaneView extends View
'activate', 'getActiveItem', toProperty: 'model'
previousActiveItem: null
attached: false
initialize: (@model) ->
constructor: (@element) ->
@itemViews = $(element.itemViews)
super
setModel: (@model) ->
@subscriptions = new CompositeDisposable
@onItemAdded(item) for item in @items
@handleEvents()
handleEvents: ->
@subscriptions.add @model.observeActiveItem(@onActiveItemChanged)
@subscriptions.add @model.onDidAddItem(@onItemAdded)
@subscriptions.add @model.onDidRemoveItem(@onItemRemoved)
@subscriptions.add @model.onDidMoveItem(@onItemMoved)
@subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed)
@subscriptions.add @model.onDidActivate(@onActivated)
@subscriptions.add @model.observeActive(@onActiveStatusChanged)
@subscriptions.add @model.onDidDestroy(@onPaneDestroyed)
@subscribe this, 'focusin', => @model.focus()
@subscribe this, 'focusout', => @model.blur()
@subscribe this, 'focus', =>
@activeView?.focus()
false
afterAttach: ->
@container ?= @closest('.panes').view()
@trigger('pane:attached', [this]) unless @attached
@attached = true
@command 'pane:save-items', => @saveItems()
@command 'pane:show-next-item', => @activateNextItem()
@command 'pane:show-previous-item', => @activatePreviousItem()
onPaneDestroyed: =>
@container?.trigger 'pane:removed', [this]
@subscriptions.dispose()
@command 'pane:show-item-1', => @activateItemAtIndex(0)
@command 'pane:show-item-2', => @activateItemAtIndex(1)
@command 'pane:show-item-3', => @activateItemAtIndex(2)
@command 'pane:show-item-4', => @activateItemAtIndex(3)
@command 'pane:show-item-5', => @activateItemAtIndex(4)
@command 'pane:show-item-6', => @activateItemAtIndex(5)
@command 'pane:show-item-7', => @activateItemAtIndex(6)
@command 'pane:show-item-8', => @activateItemAtIndex(7)
@command 'pane:show-item-9', => @activateItemAtIndex(8)
@command 'pane:split-left', => @model.splitLeft(copyActiveItem: true)
@command 'pane:split-right', => @model.splitRight(copyActiveItem: true)
@command 'pane:split-up', => @model.splitUp(copyActiveItem: true)
@command 'pane:split-down', => @model.splitDown(copyActiveItem: true)
@command 'pane:close', =>
@model.destroyItems()
@model.destroy()
@command 'pane:close-other-items', => @destroyInactiveItems()
remove: ->
@model.destroy() unless @model.isDestroyed()
# Essential: Returns the {Pane} model underlying this pane view
getModel: -> @model
@@ -109,23 +87,10 @@ class PaneView extends View
deprecate("Use PaneView::activatePreviousItem instead")
@activatePreviousItem()
afterAttach: (onDom) ->
@focus() if @model.focused and onDom
return if @attached
@container = @closest('.panes').view()
@attached = true
@trigger 'pane:attached', [this]
onActivated: =>
@focus() unless @hasFocus()
onActiveStatusChanged: (active) =>
if active
@addClass('active')
@trigger 'pane:became-active'
else
@removeClass('active')
@trigger 'pane:became-inactive'
# Public: Returns the next pane, ordered by creation.
@@ -179,23 +144,12 @@ class PaneView extends View
@trigger 'pane:item-added', [item, index]
onItemRemoved: ({item, index, destroyed}) =>
if item instanceof $
viewToRemove = item
else
viewToRemove = @model.getView(item).__spacePenView
if viewToRemove?
if destroyed
viewToRemove.remove()
else
viewToRemove.detach()
@trigger 'pane:item-removed', [item, index]
onItemMoved: ({item, newIndex}) =>
@trigger 'pane:item-moved', [item, newIndex]
onBeforeItemDestroyed: (item) =>
onBeforeItemDestroyed: ({item}) =>
@unsubscribe(item) if typeof item.off is 'function'
@trigger 'pane:before-item-destroyed', [item]
@@ -215,17 +169,7 @@ class PaneView extends View
splitDown: (items...) -> @model.getView(@model.splitDown({items})).__spacePenView
# Public: Get the container view housing this pane.
#
# Returns a {View}.
getContainer: ->
@closest('.panes').view()
getContainer: -> @closest('.panes').view()
beforeRemove: ->
@subscriptions.dispose()
@model.destroy() unless @model.isDestroyed()
remove: (selector, keepData) ->
return super if keepData
@unsubscribe()
super
focus: ->
@element.focus()
+3 -5
Ver Arquivo
@@ -53,9 +53,6 @@ class Pane extends Model
params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri
params
# Called by the view layer to construct a view for this model.
getViewClass: -> PaneView ?= require './pane-view'
getView: (object) ->
@container.getView(object)
@@ -238,6 +235,8 @@ class Pane extends Model
@focused = false
true # if this is called from an event handler, don't cancel it
isFocused: -> @focused
getPanes: -> [this]
###
@@ -414,9 +413,8 @@ class Pane extends Model
@destroyItem(item) for item in @getItems() when item isnt @activeItem
promptToSaveItem: (item) ->
return true unless item.shouldPromptToSave?()
return true unless typeof item.getUri is 'function' and item.shouldPromptToSave?()
uri = item.getUri()
chosen = atom.confirm
message: "'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
detailedMessage: "Your changes will be lost if you close this item without saving."
+5 -2
Ver Arquivo
@@ -1,4 +1,5 @@
CSON = require 'season'
{CompositeDisposable} = require 'event-kit'
module.exports =
class ScopedProperties
@@ -10,10 +11,12 @@ class ScopedProperties
callback(null, new ScopedProperties(scopedPropertiesPath, scopedProperties))
constructor: (@path, @scopedProperties) ->
@propertyDisposable = new CompositeDisposable
activate: ->
for selector, properties of @scopedProperties
atom.syntax.addProperties(@path, selector, properties)
@propertyDisposable.add atom.config.addScopedSettings(@path, selector, properties)
return
deactivate: ->
atom.syntax.removeProperties(@path)
@propertyDisposable.dispose()
+11 -42
Ver Arquivo
@@ -28,7 +28,6 @@ class Syntax extends GrammarRegistry
constructor: ->
super(maxTokensPerLine: 100)
@propertyStore = new ScopedPropertyStore
serialize: ->
{deserializer: @constructor.name, @grammarOverridesByPath}
@@ -36,52 +35,22 @@ class Syntax extends GrammarRegistry
createToken: (value, scopes) -> new Token({value, scopes})
# Deprecated: Used by settings-view to display snippets for packages
@::accessor 'scopedProperties', ->
deprecate("Use Syntax::getProperty instead")
@propertyStore.propertySets
@::accessor 'propertyStore', ->
deprecate("Do not use this. Use a public method on Config")
atom.config.scopedSettingsStore
addProperties: (args...) ->
name = args.shift() if args.length > 2
[selector, properties] = args
propertiesBySelector = {}
propertiesBySelector[selector] = properties
@propertyStore.addProperties(name, propertiesBySelector)
deprecate 'Consider using atom.config.set() instead. A direct (but private) replacement is available at atom.config.addScopedSettings().'
atom.config.addScopedSettings(args...)
removeProperties: (name) ->
@propertyStore.removeProperties(name)
deprecate 'atom.config.addScopedSettings() now returns a disposable you can call .dispose() on'
atom.config.scopedSettingsStore.removeProperties(name)
clearProperties: ->
@propertyStore = new ScopedPropertyStore
# Public: Get a property for the given scope and key path.
#
# ## Examples
#
# ```coffee
# comment = atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')
# console.log(comment) # '# '
# ```
#
# * `scope` An {Array} of {String} scopes.
# * `keyPath` A {String} key path.
#
# Returns a {String} property value or undefined.
getProperty: (scope, keyPath) ->
scopeChain = scope
.map (scope) ->
scope = ".#{scope}" unless scope[0] is '.'
scope
.join(' ')
@propertyStore.getPropertyValue(scopeChain, keyPath)
deprecate 'A direct (but private) replacement is available at atom.config.getRawScopedValue().'
atom.config.getRawScopedValue(scope, keyPath)
propertiesForScope: (scope, keyPath) ->
scopeChain = scope
.map (scope) ->
scope = ".#{scope}" unless scope[0] is '.'
scope
.join(' ')
@propertyStore.getProperties(scopeChain, keyPath)
cssSelectorFromScopeSelector: (scopeSelector) ->
new ScopeSelector(scopeSelector).toCssSelector()
deprecate 'A direct (but private) replacement is available at atom.config.scopedSettingsForScopeDescriptor().'
atom.config.settingsForScopeDescriptor(scope, keyPath)
+6
Ver Arquivo
@@ -9,3 +9,9 @@ atom.initialize()
atom.startEditorWindow()
window.atom.loadTime = Date.now() - startTime
console.log "Window load time: #{atom.getWindowLoadTime()}ms"
# Workaround for focus getting cleared upon window creation
windowFocused = ->
window.removeEventListener('focus', windowFocused)
setTimeout (-> document.querySelector('.workspace').focus()), 0
window.addEventListener('focus', windowFocused)
+1 -1
Ver Arquivo
@@ -37,7 +37,7 @@ class WindowEventHandler
atom.workspace?.open(pathToOpen, {initialLine, initialColumn})
@subscribe $(window), 'beforeunload', =>
confirmed = atom.workspaceView?.confirmClose()
confirmed = atom.workspace?.confirmClose()
atom.hide() if confirmed and not @reloadRequested and atom.getCurrentWindow().isWebViewFocused()
@reloadRequested = false
+143
Ver Arquivo
@@ -0,0 +1,143 @@
ipc = require 'ipc'
path = require 'path'
{Disposable, CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
scrollbarStyle = require 'scrollbar-style'
{callAttachHooks} = require 'space-pen'
WorkspaceView = null
module.exports =
class WorkspaceElement extends HTMLElement
createdCallback: ->
@subscriptions = new CompositeDisposable
atom.commands.setRootNode(this)
@initializeContent()
@observeScrollbarStyle()
@observeTextEditorFontConfig()
@createSpacePenShim()
attachedCallback: ->
callAttachHooks(this)
@focus()
detachedCallback: ->
@model.destroy()
initializeContent: ->
@classList.add 'workspace'
@setAttribute 'tabindex', -1
@verticalAxis = document.createElement('div')
@verticalAxis.classList.add('vertical')
@horizontalAxis = document.createElement('div')
@horizontalAxis.classList.add('horizontal')
@horizontalAxis.appendChild(@verticalAxis)
@appendChild(@horizontalAxis)
observeScrollbarStyle: ->
@subscriptions.add scrollbarStyle.onValue (style) =>
switch style
when 'legacy'
@classList.remove('scrollbars-visible-when-scrolling')
@classList.add("scrollbars-visible-always")
when 'overlay'
@classList.remove('scrollbars-visible-always')
@classList.add("scrollbars-visible-when-scrolling")
observeTextEditorFontConfig: ->
@subscriptions.add atom.config.observe 'editor.fontSize', @setTextEditorFontSize
@subscriptions.add atom.config.observe 'editor.fontFamily', @setTextEditorFontFamily
@subscriptions.add atom.config.observe 'editor.lineHeight', @setTextEditorLineHeight
createSpacePenShim: ->
WorkspaceView ?= require './workspace-view'
@__spacePenView = new WorkspaceView(this)
addCommands = (handlersByName) =>
for name, handler of handlersByName
do (handler) =>
@__spacePenView.command name, => handler.apply(this, arguments)
addCommands(
'window:increase-font-size': -> @getModel().increaseFontSize()
'window:decrease-font-size': -> @getModel().decreaseFontSize()
'window:reset-font-size': -> @getModel().resetFontSize()
'application:about': -> ipc.send('command', 'application:about')
'application:run-all-specs': -> ipc.send('command', 'application:run-all-specs')
'application:run-benchmarks': -> ipc.send('command', 'application:run-benchmarks')
'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: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': -> ipc.send('run-package-specs', path.join(atom.project.getPath(), 'spec'))
'window:focus-next-pane': -> @getModel().activateNextPane()
'window:focus-previous-pane': -> @getModel().activatePreviousPane()
'window:focus-pane-above': -> @focusPaneViewAbove()
'window:focus-pane-below': -> @focusPaneViewBelow()
'window:focus-pane-on-left': -> @focusPaneViewOnLeft()
'window:focus-pane-on-right': -> @focusPaneViewOnRight()
'window:save-all': -> @getModel().saveAll()
'window:toggle-invisibles': -> atom.config.toggle("editor.showInvisibles")
'window:log-deprecation-warnings': -> Grim.logDeprecationWarnings()
'window:toggle-auto-indent': -> atom.config.toggle("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'
addCommands 'window:install-shell-commands': -> @getModel().installShellCommands()
getModel: -> @model
setModel: (@model) ->
@paneContainer = @model.getView(@model.paneContainer)
@verticalAxis.appendChild(@paneContainer)
@addEventListener 'focus', @handleFocus.bind(this)
handleWindowFocus = @handleWindowFocus.bind(this)
window.addEventListener 'focus', handleWindowFocus
@subscriptions.add(new Disposable -> window.removeEventListener 'focus', handleWindowFocus)
@__spacePenView.setModel(@model)
setTextEditorFontSize: (fontSize) ->
atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px')
setTextEditorFontFamily: (fontFamily) ->
atom.themes.updateGlobalEditorStyle('font-family', fontFamily)
setTextEditorLineHeight: (lineHeight) ->
atom.themes.updateGlobalEditorStyle('line-height', lineHeight)
handleFocus: (event) ->
@model.getActivePane().activate()
handleWindowFocus: (event) ->
@handleFocus(event) if document.activeElement is document.body
module.exports = WorkspaceElement = document.registerElement 'atom-workspace',
prototype: WorkspaceElement.prototype
extends: 'div'
+14 -175
Ver Arquivo
@@ -6,19 +6,12 @@ Delegator = require 'delegato'
{deprecate, logDeprecationWarnings} = require 'grim'
scrollbarStyle = require 'scrollbar-style'
{$, $$, View} = require './space-pen-extensions'
fs = require 'fs-plus'
Workspace = require './workspace'
CommandInstaller = require './command-installer'
PaneView = require './pane-view'
PaneColumnView = require './pane-column-view'
PaneRowView = require './pane-row-view'
PaneContainerView = require './pane-container-view'
TextEditor = require './text-editor'
atom.commands.add '.workspace',
'window:increase-font-size': -> @getModel().increaseFontSize()
'window:decrease-font-size': -> @getModel().decreaseFontSize()
'window:reset-font-size': -> @getModel().resetFontSize()
# Extended: The top-level view for the entire window. An instance of this class is
# available via the `atom.workspaceView` global.
#
@@ -64,100 +57,20 @@ class WorkspaceView extends View
'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem',
'destroyActivePane', 'increaseFontSize', 'decreaseFontSize', toProperty: 'model'
@version: 4
@content: ->
@div class: 'workspace', tabindex: -1, =>
@div class: 'horizontal', outlet: 'horizontal', =>
@div class: 'vertical', outlet: 'vertical', =>
@div class: 'panes', outlet: 'panes'
initialize: (model) ->
@model = model ? atom.workspace ? new Workspace unless @model?
@element.getModel = -> model
atom.commands.setRootNode(@[0])
panes = @model.getView(@model.paneContainer).__spacePenView
@panes.replaceWith(panes)
@panes = panes
constructor: (@element) ->
unless @element?
return atom.workspace.getView(atom.workspace).__spacePenView
super
@deprecateViewEvents()
setModel: (@model) ->
@horizontal = @find('.horizontal')
@vertical = @find('.vertical')
@panes = @find('.panes').view()
@subscribe @model.onDidOpen => @trigger 'uri-opened'
@subscribe scrollbarStyle, (style) =>
@removeClass('scrollbars-visible-always scrollbars-visible-when-scrolling')
switch style
when 'legacy'
@addClass("scrollbars-visible-always")
when 'overlay'
@addClass("scrollbars-visible-when-scrolling")
@subscribe atom.config.observe 'editor.fontSize', @setEditorFontSize
@subscribe atom.config.observe 'editor.fontFamily', @setEditorFontFamily
@subscribe atom.config.observe 'editor.lineHeight', @setEditorLineHeight
@updateTitle()
@on 'focus', (e) => @handleFocus(e)
@subscribe $(window), 'focus', (e) =>
@handleFocus(e) if document.activeElement is document.body
atom.project.onDidChangePaths => @updateTitle()
@on 'pane-container:active-pane-item-changed', => @updateTitle()
@on 'pane:active-item-title-changed', '.active.pane', => @updateTitle()
@on 'pane:active-item-modified-status-changed', '.active.pane', => @updateDocumentEdited()
@command 'application:about', -> ipc.send('command', 'application:about')
@command 'application:run-all-specs', -> ipc.send('command', 'application:run-all-specs')
@command 'application:run-benchmarks', -> ipc.send('command', 'application:run-benchmarks')
@command 'application:show-settings', -> ipc.send('command', 'application:show-settings')
@command 'application:quit', -> ipc.send('command', 'application:quit')
@command 'application:hide', -> ipc.send('command', 'application:hide')
@command 'application:hide-other-applications', -> ipc.send('command', 'application:hide-other-applications')
@command 'application:install-update', -> ipc.send('command', 'application:install-update')
@command 'application:unhide-all-applications', -> ipc.send('command', 'application:unhide-all-applications')
@command 'application:new-window', -> ipc.send('command', 'application:new-window')
@command 'application:new-file', -> ipc.send('command', 'application:new-file')
@command 'application:open', -> ipc.send('command', 'application:open')
@command 'application:open-file', -> ipc.send('command', 'application:open-file')
@command 'application:open-folder', -> ipc.send('command', 'application:open-folder')
@command 'application:open-dev', -> ipc.send('command', 'application:open-dev')
@command 'application:open-safe', -> ipc.send('command', 'application:open-safe')
@command 'application:minimize', -> ipc.send('command', 'application:minimize')
@command 'application:zoom', -> ipc.send('command', 'application:zoom')
@command 'application:bring-all-windows-to-front', -> ipc.send('command', 'application:bring-all-windows-to-front')
@command 'application:open-your-config', -> ipc.send('command', 'application:open-your-config')
@command 'application:open-your-init-script', -> ipc.send('command', 'application:open-your-init-script')
@command 'application:open-your-keymap', -> ipc.send('command', 'application:open-your-keymap')
@command 'application:open-your-snippets', -> ipc.send('command', 'application:open-your-snippets')
@command 'application:open-your-stylesheet', -> ipc.send('command', 'application:open-your-stylesheet')
@command 'application:open-license', => @model.openLicense()
if process.platform is 'darwin'
@command 'window:install-shell-commands', => @installShellCommands()
@command 'window:run-package-specs', -> ipc.send('run-package-specs', path.join(atom.project.getPaths()[0], 'spec'))
@command 'window:focus-next-pane', => @focusNextPaneView()
@command 'window:focus-previous-pane', => @focusPreviousPaneView()
@command 'window:focus-pane-above', => @focusPaneViewAbove()
@command 'window:focus-pane-below', => @focusPaneViewBelow()
@command 'window:focus-pane-on-left', => @focusPaneViewOnLeft()
@command 'window:focus-pane-on-right', => @focusPaneViewOnRight()
@command 'window:save-all', => @saveAll()
@command 'window:toggle-invisibles', -> atom.config.set("editor.showInvisibles", not atom.config.get("editor.showInvisibles"))
@command 'window:log-deprecation-warnings', -> logDeprecationWarnings()
@command 'window:toggle-auto-indent', ->
atom.config.set("editor.autoIndent", not atom.config.get("editor.autoIndent"))
@command 'pane:reopen-closed-item', => @getModel().reopenItem()
@command 'core:close', => if @getModel().getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane()
@command 'core:save', => @saveActivePaneItem()
@command 'core:save-as', => @saveActivePaneItemAs()
@deprecatedViewEvents()
beforeRemove: ->
@model?.destroy()
###
Section: Accessing the Workspace Model
@@ -309,83 +222,9 @@ class WorkspaceView extends View
Section: Private
###
afterAttach: (onDom) ->
@focus() if onDom
# Called by SpacePen
beforeRemove: ->
@model.destroy()
setEditorFontSize: (fontSize) ->
atom.themes.updateGlobalEditorStyle('font-size', fontSize + 'px')
setEditorFontFamily: (fontFamily) ->
atom.themes.updateGlobalEditorStyle('font-family', fontFamily)
setEditorLineHeight: (lineHeight) ->
atom.themes.updateGlobalEditorStyle('line-height', lineHeight)
# Install the Atom shell commands on the user's system.
installShellCommands: ->
showErrorDialog = (error) ->
installDirectory = CommandInstaller.getInstallDirectory()
atom.confirm
message: "Failed to install shell commands"
detailedMessage: error.message
resourcePath = atom.getLoadSettings().resourcePath
CommandInstaller.installAtomCommand resourcePath, true, (error) ->
if error?
showErrorDialog(error)
else
CommandInstaller.installApmCommand resourcePath, true, (error) ->
if error?
showErrorDialog(error)
else
atom.confirm
message: "Commands installed."
detailedMessage: "The shell commands `atom` and `apm` are installed."
handleFocus: ->
if @getActivePaneView()
@getActivePaneView().focus()
false
else
@updateTitle()
focusableChild = @find("[tabindex=-1]:visible:first")
if focusableChild.length
focusableChild.focus()
false
else
$(document.body).focus()
true
# Prompts to save all unsaved items
confirmClose: ->
@panes.confirmClose()
# Updates the application's title and proxy icon based on whichever file is
# open.
updateTitle: ->
if projectPath = atom.project.getPaths()[0]
if item = @getModel().getActivePaneItem()
title = "#{item.getTitle?() ? 'untitled'} - #{projectPath}"
@setTitle(title, item.getPath?())
else
@setTitle(projectPath, projectPath)
else
@setTitle('untitled')
# Sets the application's title (and the proxy icon on OS X)
setTitle: (title, proxyIconPath='') ->
document.title = title
atom.setRepresentedFilename(proxyIconPath)
# On OS X, fades the application window's proxy icon when the current file
# has been modified.
updateDocumentEdited: ->
modified = @model.getActivePaneItem()?.isModified?() ? false
atom.setDocumentEdited(modified)
@model.confirmClose()
# Get all editor views.
#
@@ -403,7 +242,7 @@ class WorkspaceView extends View
Section: Deprecated
###
deprecatedViewEvents: ->
deprecateViewEvents: ->
originalWorkspaceViewOn = @on
@on = (eventName) =>
+81 -8
Ver Arquivo
@@ -5,13 +5,13 @@ _ = require 'underscore-plus'
Q = require 'q'
Serializable = require 'serializable'
Delegator = require 'delegato'
{Emitter, Disposable} = require 'event-kit'
{Emitter, Disposable, CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
TextEditor = require './text-editor'
PaneContainer = require './pane-container'
Pane = require './pane'
ViewRegistry = require './view-registry'
WorkspaceView = null
WorkspaceElement = require './workspace-element'
# Essential: Represents the state of the user interface for the entire window.
# An instance of this class is available via the `atom.workspace` global.
@@ -30,11 +30,12 @@ class Workspace extends Model
@delegatesProperty 'activePane', 'activePaneItem', toProperty: 'paneContainer'
@properties
viewRegistry: null
paneContainer: null
fullScreen: false
destroyedItemUris: -> []
constructor: ->
constructor: (params) ->
super
@emitter = new Emitter
@@ -44,6 +45,8 @@ class Workspace extends Model
@paneContainer ?= new PaneContainer({@viewRegistry})
@paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed)
@subscribeToActiveItem()
@addOpener (filePath) =>
switch filePath
when 'atom://.atom/stylesheet'
@@ -55,6 +58,10 @@ class Workspace extends Model
when 'atom://.atom/init-script'
@open(atom.getUserInitScriptPath())
@addViewProvider
modelConstructor: Workspace
viewConstructor: WorkspaceElement
# Called by the Serializable mixin during deserialization
deserializeParams: (params) ->
for packageName in params.packagesWithActiveGrammars ? []
@@ -71,9 +78,6 @@ class Workspace extends Model
fullScreen: atom.isFullScreen()
packagesWithActiveGrammars: @getPackageNamesWithActiveGrammars()
getViewClass: ->
WorkspaceView ?= require './workspace-view'
getPackageNamesWithActiveGrammars: ->
packageNames = []
addGrammar = ({includedGrammarScopes, packageName}={}) ->
@@ -97,6 +101,58 @@ class Workspace extends Model
editorAdded: (editor) ->
@emit 'editor-created', editor
installShellCommands: ->
require('./command-installer').installShellCommandsInteractively()
subscribeToActiveItem: ->
@updateWindowTitle()
@updateDocumentEdited()
atom.project.onDidChangePaths @updateWindowTitle
@observeActivePaneItem (item) =>
@updateWindowTitle()
@updateDocumentEdited()
@activeItemSubscriptions?.dispose()
@activeItemSubscriptions = new CompositeDisposable
if typeof item?.onDidChangeTitle is 'function'
titleSubscription = item.onDidChangeTitle(@updateWindowTitle)
else if typeof item?.on is 'function'
titleSubscription = item.on('title-changed', @updateWindowTitle)
unless typeof titleSubscription?.dispose is 'function'
titleSubscription = new Disposable => item.off('title-changed', @updateWindowTitle)
if typeof item?.onDidChangeModified is 'function'
modifiedSubscription = item.onDidChangeModified(@updateDocumentEdited)
else if typeof item?.on? is 'function'
modifiedSubscription = item.on('modified-status-changed', @updateDocumentEdited)
unless typeof modifiedSubscription?.dispose is 'function'
modifiedSubscription = new Disposable => item.off('modified-status-changed', @updateDocumentEdited)
@activeItemSubscriptions.add(titleSubscription) if titleSubscription?
@activeItemSubscriptions.add(modifiedSubscription) if modifiedSubscription?
# Updates the application's title and proxy icon based on whichever file is
# open.
updateWindowTitle: =>
if projectPath = atom.project?.getPaths()[0]
if item = @getActivePaneItem()
document.title = "#{item.getTitle?() ? 'untitled'} - #{projectPath}"
atom.setRepresentedFilename(item.getPath?() ? projectPath)
else
document.title = projectPath
atom.setRepresentedFilename(projectPath)
else
document.title = 'untitled'
atom.setRepresentedFilename('')
# On OS X, fades the application window's proxy icon when the current file
# has been modified.
updateDocumentEdited: =>
modified = @getActivePaneItem()?.isModified?() ? false
atom.setDocumentEdited(modified)
###
Section: Event Subscription
###
@@ -113,8 +169,8 @@ class Workspace extends Model
callback(textEditor) for textEditor in @getTextEditors()
@onDidAddTextEditor ({textEditor}) -> callback(textEditor)
# Essential: Invoke the given callback with all current and future panes items in
# the workspace.
# Essential: Invoke the given callback with all current and future panes items
# in the workspace.
#
# * `callback` {Function} to be called with current and future pane items.
# * `item` An item that is present in {::getPaneItems} at the time of
@@ -132,6 +188,15 @@ class Workspace extends Model
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback)
# Essential: Invoke the given callback with the current active pane item and
# with all future active pane items in the workspace.
#
# * `callback` {Function} to be called when the active pane item changes.
# * `item` The current active pane item.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
observeActivePaneItem: (callback) -> @paneContainer.observeActivePaneItem(callback)
# Essential: Invoke the given callback whenever an item is opened. Unlike
# {::onDidAddPaneItem}, observers will be notified for items that are already
# present in the workspace when they are reopened.
@@ -417,6 +482,9 @@ class Workspace extends Model
saveAll: ->
@paneContainer.saveAll()
confirmClose: ->
@paneContainer.confirmClose()
# Save the active pane item.
#
# If the active pane item currently has a URI according to the item's
@@ -477,6 +545,10 @@ class Workspace extends Model
destroyActivePane: ->
@activePane?.destroy()
# Destroy the active pane item or the active pane if it is empty.
destroyActivePaneItemOrEmptyPane: ->
if @getActivePaneItem()? then @destroyActivePaneItem() else @destroyActivePane()
# Increase the editor font size by 1px.
increaseFontSize: ->
atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1)
@@ -503,6 +575,7 @@ class Workspace extends Model
# Called by Model superclass when destroyed
destroyed: ->
@paneContainer.destroy()
@activeItemSubscriptions?.dispose()
###
Section: View Management