diff --git a/.atom/bundles/Readme.md b/.atom/bundles/Readme.md
deleted file mode 100644
index 8f53fde96..000000000
--- a/.atom/bundles/Readme.md
+++ /dev/null
@@ -1 +0,0 @@
-Put TextMate bundles in this directory
diff --git a/.atom/packages/Readme.md b/.atom/packages/Readme.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/.atom/themes/IR_Black.tmTheme b/.atom/themes/IR_Black.tmTheme
new file mode 100644
index 000000000..cbc18d0b9
--- /dev/null
+++ b/.atom/themes/IR_Black.tmTheme
@@ -0,0 +1,810 @@
+
+
+
+
+ name
+ IR_Black
+ settings
+
+
+ settings
+
+ background
+ #000000
+ caret
+ #FFFFFF
+ foreground
+ #EDEDED
+ invisibles
+ #CAE2FB3D
+ lineHighlight
+ #FFFFFF24
+ selection
+ #333333
+
+
+
+ name
+ Comment
+ scope
+ comment
+ settings
+
+ fontStyle
+
+ foreground
+ #7C7C7C
+
+
+
+ name
+ Entity
+ scope
+ entity
+ settings
+
+ fontStyle
+
+ foreground
+ #FFD2A7
+
+
+
+ name
+ Keyword
+ scope
+ keyword
+ settings
+
+ fontStyle
+
+ foreground
+ #96CBFE
+
+
+
+ name
+ Keyword.control
+ scope
+ keyword.control
+ settings
+
+ fontStyle
+
+ foreground
+ #96CBFE
+
+
+
+ name
+ Keyword.Operator
+ scope
+ keyword.operator
+ settings
+
+ foreground
+ #EDEDED
+
+
+
+ name
+ Class
+ scope
+ entity.name.type
+ settings
+
+ fontStyle
+ underline
+ foreground
+ #FFFFB6
+
+
+
+ name
+ Support
+ scope
+ support
+ settings
+
+ fontStyle
+
+ foreground
+ #FFFFB6
+
+
+
+ name
+ Storage
+ scope
+ storage
+ settings
+
+ fontStyle
+
+ foreground
+ #CFCB90
+
+
+
+ name
+ Storage.modifier
+ scope
+ storage.modifier
+ settings
+
+ fontStyle
+
+ foreground
+ #96CBFE
+
+
+
+ name
+ Constant
+ scope
+ constant
+ settings
+
+ fontStyle
+
+ foreground
+ #99CC99
+
+
+
+ name
+ String
+ scope
+ string
+ settings
+
+ fontStyle
+ bold
+ foreground
+ #A8FF60
+
+
+
+ name
+ Number
+ scope
+ constant.numeric
+ settings
+
+ fontStyle
+ bold
+ foreground
+ #FF73FD
+
+
+
+ name
+ Punctuation
+ scope
+ punctuation
+ settings
+
+ fontStyle
+
+
+
+
+ name
+ Variable
+ scope
+ variable
+ settings
+
+ fontStyle
+
+ foreground
+ #C6C5FE
+
+
+
+ name
+ Invalid – Deprecated
+ scope
+ invalid.deprecated
+ settings
+
+ fontStyle
+ italic underline
+ foreground
+ #FD5FF1
+
+
+
+ name
+ Invalid – Illegal
+ scope
+ invalid.illegal
+ settings
+
+ background
+ #562D56BF
+ foreground
+ #FD5FF1
+
+
+
+ name
+ -----------------------------------
+ settings
+
+
+
+ name
+ ♦ Embedded Source (Bright)
+ scope
+ text source
+ settings
+
+ background
+ #B1B3BA08
+ fontStyle
+
+
+
+
+ name
+ ♦ Entity inherited-class
+ scope
+ entity.other.inherited-class
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #9B5C2E
+
+
+
+ name
+ ♦ String embedded-variable
+ scope
+ source string source
+ settings
+
+ fontStyle
+
+ foreground
+ #EDEDED
+
+
+
+ name
+ ♦ String punctuation
+ scope
+ source string source punctuation.section.embedded
+ settings
+
+ fontStyle
+
+ foreground
+ #00A0A0
+
+
+
+ name
+ ♦ String constant
+ scope
+ string constant
+ settings
+
+ fontStyle
+
+ foreground
+ #00A0A0
+
+
+
+ name
+ ♦ String.regexp
+ scope
+ string.regexp
+ settings
+
+ foreground
+ #E9C062
+
+
+
+ name
+ ♦ String.regexp.«special»
+ scope
+ string.regexp constant.character.escape, string.regexp source.ruby.embedded, string.regexp string.regexp.arbitrary-repitition
+ settings
+
+ fontStyle
+
+ foreground
+ #FF8000
+
+
+
+ name
+ ♦ String.regexp.group
+ scope
+ string.regexp.group
+ settings
+
+ background
+ #FFFFFF0F
+ fontStyle
+
+ foreground
+ #C6A24F
+
+
+
+ name
+ ♦ String.regexp.character-class
+ scope
+ string.regexp.character-class
+ settings
+
+ fontStyle
+
+ foreground
+ #B18A3D
+
+
+
+ name
+ ♦ String variable
+ scope
+ string variable
+ settings
+
+ fontStyle
+
+ foreground
+ #8A9A95
+
+
+
+ name
+ ♦ Support.function
+ scope
+ support.function
+ settings
+
+ fontStyle
+
+ foreground
+ #DAD085
+
+
+
+ name
+ ♦ Support.constant
+ scope
+ support.constant
+ settings
+
+ fontStyle
+
+ foreground
+ #FFD2A7
+
+
+
+ name
+ c C/C++ Preprocessor Line
+ scope
+ meta.preprocessor.c
+ settings
+
+ foreground
+ #8996A8
+
+
+
+ name
+ c C/C++ Preprocessor Directive
+ scope
+ meta.preprocessor.c keyword
+ settings
+
+ fontStyle
+
+ foreground
+ #AFC4DB
+
+
+
+ name
+ j Cast
+ scope
+ meta.cast
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #676767
+
+
+
+ name
+ ✘ Doctype/XML Processing
+ scope
+ meta.sgml.html meta.doctype, meta.sgml.html meta.doctype entity, meta.sgml.html meta.doctype string, meta.xml-processing, meta.xml-processing entity, meta.xml-processing string
+ settings
+
+ foreground
+ #494949
+
+
+
+ name
+ ✘ Meta.tag.«all»
+ scope
+ meta.tag, meta.tag entity
+ settings
+
+ fontStyle
+ bold
+ foreground
+ #96CBFE
+
+
+
+ name
+ ✘ Meta.tag.inline
+ scope
+ source entity.name.tag, source entity.other.attribute-name, meta.tag.inline, meta.tag.inline entity
+ settings
+
+ fontStyle
+
+ foreground
+ #96CBFE
+
+
+
+ name
+ ✘ Meta.tag.attribute-name
+ scope
+ entity.other.attribute-name
+ settings
+
+ fontStyle
+
+ foreground
+ #FFD7B1
+
+
+
+ name
+ ✘ Namespaces
+ scope
+ entity.name.tag.namespace, entity.other.attribute-name.namespace
+ settings
+
+ fontStyle
+
+ foreground
+ #E18964
+
+
+
+ name
+ § css tag-name
+ scope
+ meta.selector.css entity.name.tag
+ settings
+
+ fontStyle
+ underline
+ foreground
+ #96CBFE
+
+
+
+ name
+ § css:pseudo-class
+ scope
+ meta.selector.css entity.other.attribute-name.tag.pseudo-class
+ settings
+
+ fontStyle
+
+ foreground
+ #8F9D6A
+
+
+
+ name
+ § css#id
+ scope
+ meta.selector.css entity.other.attribute-name.id
+ settings
+
+ fontStyle
+
+ foreground
+ #8B98AB
+
+
+
+ name
+ § css.class
+ scope
+ meta.selector.css entity.other.attribute-name.class
+ settings
+
+ fontStyle
+
+ foreground
+ #62B1FE
+
+
+
+ name
+ § css property-name:
+ scope
+ support.type.property-name.css
+ settings
+
+ foreground
+ #EDEDED
+
+
+
+ name
+ § css property-value;
+ scope
+ meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css
+ settings
+
+ fontStyle
+
+ foreground
+ #F9EE98
+
+
+
+ name
+ § css @at-rule
+ scope
+ meta.preprocessor.at-rule keyword.control.at-rule
+ settings
+
+ foreground
+ #8693A5
+
+
+
+ name
+ § css additional-constants
+ scope
+ meta.property-value support.constant.named-color.css, meta.property-value constant
+ settings
+
+ fontStyle
+
+ foreground
+ #87C38A
+
+
+
+ name
+ § css constructor.argument
+ scope
+ meta.constructor.argument.css
+ settings
+
+ foreground
+ #8F9D6A
+
+
+
+ name
+ ⎇ diff.header
+ scope
+ meta.diff, meta.diff.header
+ settings
+
+ background
+ #0E2231
+ fontStyle
+ italic
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.deleted
+ scope
+ markup.deleted
+ settings
+
+ background
+ #420E09
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.changed
+ scope
+ markup.changed
+ settings
+
+ background
+ #4A410D
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.inserted
+ scope
+ markup.inserted
+ settings
+
+ background
+ #253B22
+ foreground
+ #F8F8F8
+
+
+
+ name
+ --------------------------------
+ settings
+
+
+
+ name
+ Markup: Italic
+ scope
+ markup.italic
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #E9C062
+
+
+
+ name
+ Markup: Bold
+ scope
+ markup.bold
+ settings
+
+ fontStyle
+ bold
+ foreground
+ #E9C062
+
+
+
+ name
+ Markup: Underline
+ scope
+ markup.underline
+ settings
+
+ fontStyle
+ underline
+ foreground
+ #E18964
+
+
+
+ name
+ Markup: Quote
+ scope
+ markup.quote
+ settings
+
+ background
+ #FEE09C12
+ fontStyle
+ italic
+ foreground
+ #E1D4B9
+
+
+
+ name
+ Markup: Heading
+ scope
+ markup.heading, markup.heading entity
+ settings
+
+ background
+ #632D04
+ fontStyle
+
+ foreground
+ #FEDCC5
+
+
+
+ name
+ Markup: List
+ scope
+ markup.list
+ settings
+
+ foreground
+ #E1D4B9
+
+
+
+ name
+ Markup: Raw
+ scope
+ markup.raw
+ settings
+
+ background
+ #B1B3BA08
+ fontStyle
+
+ foreground
+ #578BB3
+
+
+
+ name
+ Markup: Comment
+ scope
+ markup comment
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #F67B37
+
+
+
+ name
+ Markup: Separator
+ scope
+ meta.separator
+ settings
+
+ background
+ #242424
+ foreground
+ #60A633
+
+
+
+ name
+ Log Entry
+ scope
+ meta.line.entry.logfile, meta.line.exit.logfile
+ settings
+
+ background
+ #EEEEEE29
+
+
+
+ name
+ Log Entry Error
+ scope
+ meta.line.error.logfile
+ settings
+
+ background
+ #751012
+
+
+
+ uuid
+ D039AEA9-9DD2-4237-A963-E84494B0B3FB
+
+
diff --git a/Rakefile b/Rakefile
index 4f67d75f2..e85ba30a5 100644
--- a/Rakefile
+++ b/Rakefile
@@ -60,6 +60,20 @@ end
desc "Creates .atom file if non exists"
task "create-dot-atom" do
+ # Migration: If there is still a bundle path, rename it to packages
+ if File.exists?(DOT_ATOM_PATH) and File.exists?(File.join(DOT_ATOM_PATH, "bundles"))
+ if File.exists?(File.join(DOT_ATOM_PATH, "packages"))
+ `mv #{File.join(DOT_ATOM_PATH, "bundles", "*")} #{File.join(DOT_ATOM_PATH, "packages")}`
+ $stderr.puts "WARNING: Bundles from ~/.atom/bundles were moved to ~/.atom/packages"
+ else
+ `mv #{File.join(DOT_ATOM_PATH, "bundles")} #{File.join(DOT_ATOM_PATH, "packages")}`
+ $stderr.puts "WARNING: ~/.atom/bundles was moved to ~/.atom/packages"
+ end
+ end
+
+ # Migration: remove files that are no longer needed
+ `rm -rf #{File.join(DOT_ATOM_PATH, 'default-config.coffee')}`
+
dot_atom_template_path = ATOM_SRC_PATH + "/.atom"
replace_dot_atom = false
next if File.exists?(DOT_ATOM_PATH)
@@ -67,12 +81,8 @@ task "create-dot-atom" do
`rm -rf "#{DOT_ATOM_PATH}"`
`mkdir "#{DOT_ATOM_PATH}"`
`cp "#{dot_atom_template_path}/atom.coffee" "#{DOT_ATOM_PATH}"`
- `cp "#{dot_atom_template_path}/bundles" "#{DOT_ATOM_PATH}"`
-
- for path in Dir.entries(dot_atom_template_path)
- next if ["..", ".", "atom.coffee", "bundles"].include? path
- `ln -s "#{dot_atom_template_path}/#{path}" "#{DOT_ATOM_PATH}"`
- end
+ `cp "#{dot_atom_template_path}/packages" "#{DOT_ATOM_PATH}"`
+ `cp -r "#{dot_atom_template_path}/themes" "#{DOT_ATOM_PATH}"`
end
desc "Clone default bundles into .atom directory"
@@ -96,7 +106,7 @@ task "clone-default-bundles" => "create-dot-atom" do
for bundle_url, sha in bundles
bundle_dir = bundle_url[/([^\/]+?)(\.git)?$/, 1]
- dest_path = File.join(DOT_ATOM_PATH, "bundles", bundle_dir)
+ dest_path = File.join(DOT_ATOM_PATH, "packages", bundle_dir)
if File.exists? dest_path
`cd #{dest_path} && git fetch --quiet`
else
@@ -110,6 +120,8 @@ end
desc "Clean build Atom via `xcodebuild`"
task :clean do
output = `xcodebuild clean`
+ `rm -rf #{application_path()}`
+ `rm -rf #{BUILD_DIR}`
end
desc "Run Atom"
@@ -135,7 +147,7 @@ task :benchmark do
end
task :nof do
- system %{find . -name *spec.coffee | grep -v atom-build | xargs sed -E -i "" "s/f+(it|describe) +(['\\"])/\\1 \\2/g"}
+ system %{find . -name *spec.coffee | grep -v #{BUILD_DIR} | xargs sed -E -i "" "s/f+(it|describe) +(['\\"])/\\1 \\2/g"}
end
task :tags do
diff --git a/benchmark/benchmark-helper.coffee b/benchmark/benchmark-helper.coffee
index fea602b9b..663ebd344 100644
--- a/benchmark/benchmark-helper.coffee
+++ b/benchmark/benchmark-helper.coffee
@@ -11,12 +11,14 @@ TextMateTheme = require 'text-mate-theme'
require 'window'
requireStylesheet "jasmine.css"
+# Load TextMate bundles, which specs rely on (but not other packages)
+atom.loadPackages(atom.getAvailableTextMateBundles())
+
beforeEach ->
- # don't load user configuration
+ # reset config after each benchmark; don't load or save from/to `config.json`
window.config = new Config()
spyOn(config, 'load')
spyOn(config, 'save')
- config.assignDefaults()
keymap = new Keymap
keymap.bindDefaultKeys()
diff --git a/docs/configuring-and-extending.md b/docs/configuring-and-extending.md
index 8423c6fd7..033618650 100644
--- a/docs/configuring-and-extending.md
+++ b/docs/configuring-and-extending.md
@@ -78,58 +78,6 @@ ConfigObserver = require 'config-observer'
_.extend MyClass.prototype, ConfigObserver
```
-## Scoped Config Settings (Not Yet Implemented)
-
-Users and extension authors can provide language-specific behavior by employing
-*scoped configuration keys*. By associating key values with a specific scope,
-you can make Atom behave differently in different contexts. For example, if you
-want Atom to auto-indent pasted text in some languages but not others, you can
-give the `autoIndentPastedText` key a different value under a scope selector:
-
-```coffeescript
-# in config.cson
-editor:
- autoIndentPastedText: true
-scopes:
- ".source.coffee":
- editor:
- autoIndentPastedText: false
-```
-
-Scope selectors are placed under the `scope` key at the top-level of the
-configuration file. The values you specify for keys under a selector will
-override global values in that specific scope. Any basic CSS 3 selector is
-permitted, but you should leave out element names to make your keys accessible
-outside the view layer.
-
-### Reading Scoped Config Settings
-
-Use the `config.inScope` method to the read keys with the most specific selector
-match:
-
-```coffeescript
-scope = [".source.coffee", ".meta.class.instance.constructor"]
-config.inScope(scope).get "editor.lineComment"
-```
-
-Pass `.inScope` an array of scope descriptors, which describes a specific
-element. This is frequently useful when you get the nested scopes for a position
-in the buffer based on its syntax. You can also pass an actual DOM element
-to use its nesting within the DOM as fodder for the scope selectors (†).
-
-```coffeescript
-config.inScope(fuzzyFinder.miniEditor).get("editor.fontSize")
-```
-
-`observeConfig` can take a scope as its first argument:
-
-```
-@observeConfig scope, "editor.autoIndentPastedText", -> # ...
-```
-
-†: Matching DOM elements fits cleanly into this scheme, but I can't think of a
- use for it currently. Let's keep it in the back of our minds though.
-
# Themes (Not Yet Implemented)
## Selecting A Theme
@@ -162,7 +110,7 @@ folder, which can contain multiple stylesheets along with an optional
package.json:
```json
{
- "stylesheets": ["core", "editor", "tree-view"]
+ "stylesheets": ["core.css", "editor.less", "tree-view.css"]
}
```
diff --git a/script/copy-files-to-bundle b/script/copy-files-to-bundle
index f850405a7..659c87e04 100755
--- a/script/copy-files-to-bundle
+++ b/script/copy-files-to-bundle
@@ -29,4 +29,4 @@ for COFFEE_FILE in $COFFEE_FILES; do
done;
# Copy non-coffee files into bundle
-rsync --archive --recursive --exclude="src/**/*.coffee" src static vendor spec benchmark themes "$RESOUCES_PATH"
+rsync --archive --recursive --exclude="src/**/*.coffee" src static vendor spec benchmark "$RESOUCES_PATH"
diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee
index 501a0fa07..e40224d61 100644
--- a/spec/app/atom-spec.coffee
+++ b/spec/app/atom-spec.coffee
@@ -6,9 +6,9 @@ describe "the `atom` global", ->
beforeEach ->
rootView = new RootView
- extension = require "package-with-extension"
+ extension = require "package-with-module"
it "requires and activates the package's main module if it exists", ->
- spyOn(rootView, 'activateExtension').andCallThrough()
- atom.loadPackage("package-with-extension")
- expect(rootView.activateExtension).toHaveBeenCalledWith(extension)
+ spyOn(rootView, 'activatePackage').andCallThrough()
+ atom.loadPackage("package-with-module")
+ expect(rootView.activatePackage).toHaveBeenCalledWith(extension)
diff --git a/spec/app/language-mode-spec.coffee b/spec/app/language-mode-spec.coffee
index e7bdc7ea9..340f4865c 100644
--- a/spec/app/language-mode-spec.coffee
+++ b/spec/app/language-mode-spec.coffee
@@ -262,19 +262,19 @@ describe "LanguageMode", ->
expect(buffer.lineForRow(0)).toBe "/*body {"
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
expect(buffer.lineForRow(2)).toBe " width: 110%;"
- expect(buffer.lineForRow(3)).toBe "}"
+ expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
languageMode.toggleLineCommentsForBufferRows(2, 2)
expect(buffer.lineForRow(0)).toBe "/*body {"
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
- expect(buffer.lineForRow(3)).toBe "}"
+ expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
languageMode.toggleLineCommentsForBufferRows(0, 1)
expect(buffer.lineForRow(0)).toBe "body {"
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;"
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
- expect(buffer.lineForRow(3)).toBe "}"
+ expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
it "uncomments lines with leading whitespace", ->
buffer.replaceLines(2, 2, " /*width: 110%;*/")
diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee
index 3043f2731..cb3267598 100644
--- a/spec/app/root-view-spec.coffee
+++ b/spec/app/root-view-spec.coffee
@@ -125,24 +125,24 @@ describe "RootView", ->
expect(rootView.getTitle()).toBe 'untitled'
describe ".serialize()", ->
- it "absorbs exceptions that are thrown by extension serialize methods", ->
+ it "absorbs exceptions that are thrown by the package module's serialize methods", ->
spyOn(console, 'error')
- rootView.activateExtension(
+ rootView.activatePackage(
name: "bad-egg"
activate: ->
serialize: -> throw new Error("I'm broken")
)
- rootView.activateExtension(
+ rootView.activatePackage(
name: "good-egg"
activate: ->
serialize: -> "I still get called"
)
data = rootView.serialize()
- expect(data.extensionStates['good-egg']).toBe "I still get called"
- expect(data.extensionStates['bad-egg']).toBeUndefined()
+ expect(data.packageStates['good-egg']).toBe "I still get called"
+ expect(data.packageStates['bad-egg']).toBeUndefined()
expect(console.error).toHaveBeenCalled()
describe "focus", ->
@@ -393,54 +393,49 @@ describe "RootView", ->
rootView.focusNextPane()
expect(view1.focus).toHaveBeenCalled()
- describe "extensions", ->
- extension = null
+ describe "packages", ->
+ packageModule = null
beforeEach ->
- extension =
- name: 'extension'
+ packageModule =
+ name: 'package'
deactivate: ->
activate: jasmine.createSpy("activate")
serialize: -> "it worked"
- describe ".activateExtension(extension)", ->
- it "calls activate on the extension", ->
- rootView.activateExtension(extension)
- expect(extension.activate).toHaveBeenCalledWith(rootView, undefined, undefined)
+ describe ".activatePackage(packageModule)", ->
+ it "calls activate on the package module", ->
+ rootView.activatePackage(packageModule)
+ expect(packageModule.activate).toHaveBeenCalledWith(rootView, undefined)
- it "calls activate on the extension with its previous state", ->
- rootView.activateExtension(extension)
- extension.activate.reset()
+ it "calls activate on the package module with its previous state", ->
+ rootView.activatePackage(packageModule)
+ packageModule.activate.reset()
newRootView = RootView.deserialize(rootView.serialize())
- newRootView.activateExtension(extension)
- expect(extension.activate).toHaveBeenCalledWith(newRootView, "it worked", undefined)
+ newRootView.activatePackage(packageModule)
+ expect(packageModule.activate).toHaveBeenCalledWith(newRootView, "it worked")
newRootView.remove()
- it "calls activate on the extension with the config data", ->
- config = {}
- rootView.activateExtension(extension, config)
- expect(extension.activate).toHaveBeenCalledWith(rootView, undefined, config)
+ it "throws an exception if the package module has no 'name' property", ->
+ expect(-> rootView.activatePackage({ activate: -> })).toThrow()
- it "throws an exception if the extension has no 'name' property", ->
- expect(-> rootView.activateExtension({ activate: -> })).toThrow()
+ describe ".deactivatePackage(packageModule)", ->
+ it "deactivates and removes the package module from the package module map", ->
+ rootView.activatePackage(packageModule)
+ expect(rootView.packageModules[packageModule.name]).toBeTruthy()
+ spyOn(packageModule, "deactivate").andCallThrough()
+ rootView.deactivatePackage(packageModule)
+ expect(packageModule.deactivate).toHaveBeenCalled()
+ expect(rootView.packageModules[packageModule.name]).toBeFalsy()
- describe ".deactivateExtension(extension)", ->
- it "deactivates and removes the extension from the extension list", ->
- rootView.activateExtension(extension)
- expect(rootView.extensions[extension.name]).toBeTruthy()
- spyOn(extension, "deactivate").andCallThrough()
- rootView.deactivateExtension(extension)
- expect(extension.deactivate).toHaveBeenCalled()
- expect(rootView.extensions[extension.name]).toBeFalsy()
-
- it "is called when the rootView is deactivated to deactivate all extensions", ->
- rootView.activateExtension(extension)
- spyOn(rootView, "deactivateExtension").andCallThrough()
- spyOn(extension, "deactivate").andCallThrough()
+ it "is called when the rootView is deactivated to deactivate all packages", ->
+ rootView.activatePackage(packageModule)
+ spyOn(rootView, "deactivatePackage").andCallThrough()
+ spyOn(packageModule, "deactivate").andCallThrough()
rootView.deactivate()
- expect(rootView.deactivateExtension).toHaveBeenCalled()
- expect(extension.deactivate).toHaveBeenCalled()
+ expect(rootView.deactivatePackage).toHaveBeenCalled()
+ expect(packageModule.deactivate).toHaveBeenCalled()
describe "keymap wiring", ->
commandHandler = null
diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee
new file mode 100644
index 000000000..c3117fb46
--- /dev/null
+++ b/spec/app/syntax-spec.coffee
@@ -0,0 +1,19 @@
+describe "the `syntax` global", ->
+ describe ".getProperty(scopeDescriptor)", ->
+ it "returns the property with the most specific scope selector", ->
+ syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
+ syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22)
+ syntax.addProperties(".source", foo: bar: baz: 11)
+ syntax.addProperties(foo: bar: baz: 1)
+
+ expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
+ expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
+ expect(syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
+ expect(syntax.getProperty([".text"], "foo.bar.baz")).toBe 1
+
+ it "favors the most recently added properties in the event of a specificity tie", ->
+ syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
+ syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
+
+ expect(syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
+ expect(syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
diff --git a/spec/app/text-mate-bundle-spec.coffee b/spec/app/text-mate-bundle-spec.coffee
index 663db783f..c67bc50cc 100644
--- a/spec/app/text-mate-bundle-spec.coffee
+++ b/spec/app/text-mate-bundle-spec.coffee
@@ -2,14 +2,6 @@ fs = require('fs')
TextMateBundle = require 'text-mate-bundle'
describe "TextMateBundle", ->
- describe ".getPreferenceInScope(scope, preferenceName)", ->
- it "returns the preference by the given name in the given scope or undefined if there isn't one", ->
- expect(TextMateBundle.getPreferenceInScope('source.coffee', 'decreaseIndentPattern')).toBe '^\\s*(\\}|\\]|else|catch|finally)$'
- expect(TextMateBundle.getPreferenceInScope('source.coffee', 'shellVariables')).toBeDefined()
-
- it "returns the preference by the given name in the given scope for a scope registered via a comma-separated list of scopes", ->
- expect(TextMateBundle.getPreferenceInScope('source.objc++', 'shellVariables')).toBeDefined()
-
describe ".getPreferencesByScopeSelector()", ->
it "logs warning, but does not raise errors if a preference can't be parsed", ->
bundlePath = fs.join(require.resolve('fixtures'), "test.tmbundle")
diff --git a/spec/app/text-mate-theme-spec.coffee b/spec/app/text-mate-theme-spec.coffee
index d040a30df..0ea739f7e 100644
--- a/spec/app/text-mate-theme-spec.coffee
+++ b/spec/app/text-mate-theme-spec.coffee
@@ -1,29 +1,17 @@
fs = require 'fs'
plist = require 'plist'
TextMateTheme = require 'text-mate-theme'
+Theme = require 'theme'
describe "TextMateTheme", ->
- theme = null
+ [theme, themePath] = []
+
beforeEach ->
- theme = TextMateTheme.getTheme('Twilight')
+ themePath = require.resolve(fs.join('fixtures', 'test.tmTheme'))
+ [theme] = Theme.load(themePath)
- describe "@getNames()", ->
- it "returns an array of available theme names", ->
- names = TextMateTheme.getNames()
- expect(names).toContain("Twilight")
- expect(names).toContain("Blackboard")
-
- describe "@activate(name)", ->
- it "activates a theme by name", ->
- spyOn theme, 'activate'
- TextMateTheme.activate('Twilight')
- expect(theme.activate).toHaveBeenCalled()
-
- describe ".activate()", ->
- it "applies the theme's stylesheet to the current window", ->
- spyOn window, 'applyStylesheet'
- theme.activate()
- expect(window.applyStylesheet).toHaveBeenCalledWith(theme.name, theme.getStylesheet())
+ afterEach ->
+ theme.deactivate()
describe ".getRulesets()", ->
rulesets = null
diff --git a/spec/app/theme-spec.coffee b/spec/app/theme-spec.coffee
new file mode 100644
index 000000000..5990aec2c
--- /dev/null
+++ b/spec/app/theme-spec.coffee
@@ -0,0 +1,48 @@
+$ = require 'jquery'
+fs = require 'fs'
+Theme = require 'theme'
+
+describe "@load(name)", ->
+ themes = null
+
+ beforeEach ->
+ $("#jasmine-content").append $("
")
+
+ afterEach ->
+ theme.deactivate() for theme in themes
+
+ describe "TextMateTheme", ->
+ it "applies the theme's stylesheet to the current window", ->
+ expect($(".editor").css("background-color")).not.toBe("rgb(20, 20, 20)")
+
+ themePath = require.resolve(fs.join('fixtures', 'test.tmTheme'))
+ themes = Theme.load(themePath)
+ expect($(".editor").css("background-color")).toBe("rgb(20, 20, 20)")
+
+ describe "AtomTheme", ->
+ it "Loads and applies css from package.json in the correct order", ->
+ expect($(".editor").css("padding-top")).not.toBe("101px")
+ expect($(".editor").css("padding-right")).not.toBe("102px")
+ expect($(".editor").css("padding-bottom")).not.toBe("103px")
+
+ themePath = require.resolve(fs.join('fixtures', 'test-atom-theme'))
+ themes = Theme.load(themePath)
+ expect($(".editor").css("padding-top")).toBe("101px")
+ expect($(".editor").css("padding-right")).toBe("102px")
+ expect($(".editor").css("padding-bottom")).toBe("103px")
+
+ describe "when name is an array of themes", ->
+ it "loads all themes in order", ->
+ firstThemePath = require.resolve(fs.join('fixtures', 'test.tmTheme'))
+ secondThemePath = require.resolve(fs.join('fixtures', 'test-atom-theme'))
+
+ expect($(".editor").css("padding-top")).not.toBe("101px")
+ expect($(".editor").css("padding-right")).not.toBe("102px")
+ expect($(".editor").css("padding-bottom")).not.toBe("103px")
+ expect($(".editor").css("color")).not.toBe("rgb(0, 255, 0)")
+
+ themes = Theme.load([firstThemePath, secondThemePath])
+ expect($(".editor").css("padding-top")).toBe("101px")
+ expect($(".editor").css("padding-right")).toBe("102px")
+ expect($(".editor").css("padding-bottom")).toBe("103px")
+ expect($(".editor").css("color")).toBe("rgb(255, 0, 0)")
diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee
index 05e582d34..9cd9103b2 100644
--- a/spec/app/window-spec.coffee
+++ b/spec/app/window-spec.coffee
@@ -48,6 +48,16 @@ describe "Window", ->
requireStylesheet('atom.css')
expect($('head style').length).toBe lengthBefore + 1
+ describe ".disableStyleSheet(path)", ->
+ it "removes styling applied by given stylesheet path", ->
+ cssPath = require.resolve(fs.join("fixtures", "css.css"))
+
+ expect($(document.body).css('font-weight')).not.toBe("bold")
+ requireStylesheet(cssPath)
+ expect($(document.body).css('font-weight')).toBe("bold")
+ removeStylesheet(cssPath)
+ expect($(document.body).css('font-weight')).not.toBe("bold")
+
describe "before the window is unloaded", ->
it "saves the serialized state of the root view to the atom object so it can be rehydrated after reload", ->
expect(atom.getRootViewStateForPath(window.rootView.project.getPath())).toBeUndefined()
diff --git a/spec/fixtures/css.css b/spec/fixtures/css.css
index 52b8f56c6..d5ae97e68 100644
--- a/spec/fixtures/css.css
+++ b/spec/fixtures/css.css
@@ -1,4 +1,5 @@
body {
font-size: 1234px;
width: 110%;
+ font-weight: bold !important;
}
diff --git a/spec/fixtures/packages/package-with-extension/index.coffee b/spec/fixtures/packages/package-with-module/index.coffee
similarity index 100%
rename from spec/fixtures/packages/package-with-extension/index.coffee
rename to spec/fixtures/packages/package-with-module/index.coffee
diff --git a/spec/fixtures/test-atom-theme/first.css b/spec/fixtures/test-atom-theme/first.css
new file mode 100644
index 000000000..f9af1a345
--- /dev/null
+++ b/spec/fixtures/test-atom-theme/first.css
@@ -0,0 +1,7 @@
+.editor {
+ padding-top: 101px;
+ padding-right: 101px;
+ padding-bottom: 101px;
+
+ color: red;
+}
\ No newline at end of file
diff --git a/spec/fixtures/test-atom-theme/last.css b/spec/fixtures/test-atom-theme/last.css
new file mode 100644
index 000000000..c0cface8c
--- /dev/null
+++ b/spec/fixtures/test-atom-theme/last.css
@@ -0,0 +1,5 @@
+.editor {
+/* padding-top: 103px;
+ padding-right: 103px;*/
+ padding-bottom: 103px;
+}
\ No newline at end of file
diff --git a/spec/fixtures/test-atom-theme/package.json b/spec/fixtures/test-atom-theme/package.json
new file mode 100644
index 000000000..9add36774
--- /dev/null
+++ b/spec/fixtures/test-atom-theme/package.json
@@ -0,0 +1,3 @@
+{
+ "stylesheets": ["first.css", "second.css", "last.css"]
+}
\ No newline at end of file
diff --git a/spec/fixtures/test-atom-theme/second.css b/spec/fixtures/test-atom-theme/second.css
new file mode 100644
index 000000000..3ddf03add
--- /dev/null
+++ b/spec/fixtures/test-atom-theme/second.css
@@ -0,0 +1,5 @@
+.editor {
+/* padding-top: 102px;*/
+ padding-right: 102px;
+ padding-bottom: 102px;
+}
\ No newline at end of file
diff --git a/spec/fixtures/test.tmTheme b/spec/fixtures/test.tmTheme
new file mode 100644
index 000000000..a83f7ecbb
--- /dev/null
+++ b/spec/fixtures/test.tmTheme
@@ -0,0 +1,514 @@
+
+
+
+
+ author
+ Michael Sheets
+ name
+ Twilight
+ settings
+
+
+ settings
+
+ background
+ #141414
+ caret
+ #A7A7A7
+ foreground
+ #F8F8F8
+ invisibles
+ #FFFFFF40
+ lineHighlight
+ #FFFFFF08
+ selection
+ #DDF0FF33
+
+
+
+ name
+ Comment
+ scope
+ comment
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #5F5A60
+
+
+
+ name
+ Constant
+ scope
+ constant
+ settings
+
+ foreground
+ #CF6A4C
+
+
+
+ name
+ Entity
+ scope
+ entity
+ settings
+
+ fontStyle
+
+ foreground
+ #9B703F
+
+
+
+ name
+ Keyword
+ scope
+ keyword
+ settings
+
+ fontStyle
+
+ foreground
+ #CDA869
+
+
+
+ name
+ Storage
+ scope
+ storage
+ settings
+
+ fontStyle
+
+ foreground
+ #F9EE98
+
+
+
+ name
+ String
+ scope
+ string
+ settings
+
+ fontStyle
+
+ foreground
+ #8F9D6A
+
+
+
+ name
+ Support
+ scope
+ support
+ settings
+
+ fontStyle
+
+ foreground
+ #9B859D
+
+
+
+ name
+ Variable
+ scope
+ variable
+ settings
+
+ foreground
+ #7587A6
+
+
+
+ name
+ Invalid – Deprecated
+ scope
+ invalid.deprecated
+ settings
+
+ fontStyle
+ italic underline
+ foreground
+ #D2A8A1
+
+
+
+ name
+ Invalid – Illegal
+ scope
+ invalid.illegal
+ settings
+
+ background
+ #562D56BF
+ foreground
+ #F8F8F8
+
+
+
+ name
+ -----------------------------------
+ settings
+
+
+
+ name
+ ♦ Embedded Source
+ scope
+ text source
+ settings
+
+ background
+ #B0B3BA14
+
+
+
+ name
+ ♦ Embedded Source (Bright)
+ scope
+ text.html.ruby source
+ settings
+
+ background
+ #B1B3BA21
+
+
+
+ name
+ ♦ Entity inherited-class
+ scope
+ entity.other.inherited-class
+ settings
+
+ fontStyle
+ italic
+ foreground
+ #9B5C2E
+
+
+
+ name
+ ♦ String embedded-source
+ scope
+ string source
+ settings
+
+ fontStyle
+
+ foreground
+ #DAEFA3
+
+
+
+ name
+ ♦ String constant
+ scope
+ string constant
+ settings
+
+ foreground
+ #DDF2A4
+
+
+
+ name
+ ♦ String.regexp
+ scope
+ string.regexp
+ settings
+
+ fontStyle
+
+ foreground
+ #E9C062
+
+
+
+ name
+ ♦ String.regexp.«special»
+ scope
+ string.regexp constant.character.escape, string.regexp source.ruby.embedded, string.regexp string.regexp.arbitrary-repitition
+ settings
+
+ foreground
+ #CF7D34
+
+
+
+ name
+ ♦ String variable
+ scope
+ string variable
+ settings
+
+ foreground
+ #8A9A95
+
+
+
+ name
+ ♦ Support.function
+ scope
+ support.function
+ settings
+
+ fontStyle
+
+ foreground
+ #DAD085
+
+
+
+ name
+ ♦ Support.constant
+ scope
+ support.constant
+ settings
+
+ fontStyle
+
+ foreground
+ #CF6A4C
+
+
+
+ name
+ c C/C++ Preprocessor Line
+ scope
+ meta.preprocessor.c
+ settings
+
+ foreground
+ #8996A8
+
+
+
+ name
+ c C/C++ Preprocessor Directive
+ scope
+ meta.preprocessor.c keyword
+ settings
+
+ foreground
+ #AFC4DB
+
+
+
+ name
+ ✘ Doctype/XML Processing
+ scope
+ meta.tag.sgml.doctype, meta.tag.sgml.doctype entity, meta.tag.sgml.doctype string, meta.tag.preprocessor.xml, meta.tag.preprocessor.xml entity, meta.tag.preprocessor.xml string
+ settings
+
+ foreground
+ #494949
+
+
+
+ name
+ ✘ Meta.tag.«all»
+ scope
+ declaration.tag, declaration.tag entity, meta.tag, meta.tag entity
+ settings
+
+ foreground
+ #AC885B
+
+
+
+ name
+ ✘ Meta.tag.inline
+ scope
+ declaration.tag.inline, declaration.tag.inline entity, source entity.name.tag, source entity.other.attribute-name, meta.tag.inline, meta.tag.inline entity
+ settings
+
+ foreground
+ #E0C589
+
+
+
+ name
+ § css tag-name
+ scope
+ meta.selector.css entity.name.tag
+ settings
+
+ foreground
+ #CDA869
+
+
+
+ name
+ § css:pseudo-class
+ scope
+ meta.selector.css entity.other.attribute-name.tag.pseudo-class
+ settings
+
+ foreground
+ #8F9D6A
+
+
+
+ name
+ § css#id
+ scope
+ meta.selector.css entity.other.attribute-name.id
+ settings
+
+ foreground
+ #8B98AB
+
+
+
+ name
+ § css.class
+ scope
+ meta.selector.css entity.other.attribute-name.class
+ settings
+
+ foreground
+ #9B703F
+
+
+
+ name
+ § css property-name:
+ scope
+ support.type.property-name.css
+ settings
+
+ foreground
+ #C5AF75
+
+
+
+ name
+ § css property-value;
+ scope
+ meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css
+ settings
+
+ foreground
+ #F9EE98
+
+
+
+ name
+ § css @at-rule
+ scope
+ meta.preprocessor.at-rule keyword.control.at-rule
+ settings
+
+ foreground
+ #8693A5
+
+
+
+ name
+ § css additional-constants
+ scope
+ meta.property-value support.constant.named-color.css, meta.property-value constant
+ settings
+
+ foreground
+ #CA7840
+
+
+
+ name
+ § css constructor.argument
+ scope
+ meta.constructor.argument.css
+ settings
+
+ foreground
+ #8F9D6A
+
+
+
+ name
+ ⎇ diff.header
+ scope
+ meta.diff, meta.diff.header, meta.separator
+ settings
+
+ background
+ #0E2231
+ fontStyle
+ italic
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.deleted
+ scope
+ markup.deleted
+ settings
+
+ background
+ #420E09
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.changed
+ scope
+ markup.changed
+ settings
+
+ background
+ #4A410D
+ foreground
+ #F8F8F8
+
+
+
+ name
+ ⎇ diff.inserted
+ scope
+ markup.inserted
+ settings
+
+ background
+ #253B22
+ foreground
+ #F8F8F8
+
+
+
+ name
+ Markup: List
+ scope
+ markup.list
+ settings
+
+ foreground
+ #F9EE98
+
+
+
+ name
+ Markup: Heading
+ scope
+ markup.heading
+ settings
+
+ foreground
+ #CF6A4C
+
+
+
+ uuid
+ 766026CB-703D-4610-B070-8DE07D967C5F
+
+
diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee
index 33a372f02..5de43bccc 100644
--- a/spec/spec-helper.coffee
+++ b/spec/spec-helper.coffee
@@ -19,15 +19,17 @@ requireStylesheet "jasmine.css"
require.paths.unshift(require.resolve('fixtures/packages'))
+# Load TextMate bundles, which specs rely on (but not other packages)
+atom.loadPackages(atom.getAvailableTextMateBundles())
+
beforeEach ->
window.fixturesProject = new Project(require.resolve('fixtures'))
window.resetTimeouts()
- # don't load or save user configuration
+ # reset config after each spec; don't load or save from/to `config.json`
window.config = new Config()
spyOn(config, 'load')
spyOn(config, 'save')
- config.assignDefaults()
config.set "editor.fontSize", 16
# make editor display updates synchronous
diff --git a/spec/stdlib/child-process-spec.coffee b/spec/stdlib/child-process-spec.coffee
index 4c389f087..15b9be963 100644
--- a/spec/stdlib/child-process-spec.coffee
+++ b/spec/stdlib/child-process-spec.coffee
@@ -121,7 +121,7 @@ describe 'Child Processes', ->
cmd = "for i in {1..20000}; do echo $RANDOM; done"
options =
stdout: (data) -> output.push(data)
- stderr: (data) -> console.log data.length
+ stderr: (data) ->
ChildProcess.exec(cmd, options)
diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee
new file mode 100644
index 000000000..11d322461
--- /dev/null
+++ b/src/app/atom-package.coffee
@@ -0,0 +1,17 @@
+Package = require 'package'
+fs = require 'fs'
+
+module.exports =
+class AtomPackage extends Package
+ constructor: ->
+ super
+ @module = require(@path)
+ @module.name = @name
+
+ load: ->
+ try
+ rootView.activatePackage(@module)
+ extensionKeymapPath = require.resolve(fs.join(@name, "src/keymap"), verifyExistence: false)
+ require extensionKeymapPath if fs.exists(extensionKeymapPath)
+ catch e
+ console.error "Failed to load package named '#{name}'", e.stack
diff --git a/src/app/atom-theme.coffee b/src/app/atom-theme.coffee
new file mode 100644
index 000000000..531bb34ec
--- /dev/null
+++ b/src/app/atom-theme.coffee
@@ -0,0 +1,11 @@
+fs = require 'fs'
+Theme = require 'theme'
+
+module.exports =
+class AtomTheme extends Theme
+ constructor: (@path) ->
+ super
+ json = fs.read(fs.join(path, "package.json"))
+ for stylesheetName in JSON.parse(json).stylesheets
+ stylesheetPath = fs.join(@path, stylesheetName)
+ @stylesheets[stylesheetPath] = fs.read(stylesheetPath)
diff --git a/src/app/atom.coffee b/src/app/atom.coffee
index fb8a559c1..82a406bd7 100644
--- a/src/app/atom.coffee
+++ b/src/app/atom.coffee
@@ -1,5 +1,8 @@
+TextMateBundle = require("text-mate-bundle")
fs = require 'fs'
_ = require 'underscore'
+Package = require 'package'
+TextMatePackage = require 'text-mate-package'
messageIdCounter = 1
originalSendMessageToBrowserProcess = atom.sendMessageToBrowserProcess
@@ -9,18 +12,25 @@ _.extend atom,
pendingBrowserProcessCallbacks: {}
+ getAvailablePackages: ->
+ allPackageNames = []
+ for packageDirPath in config.packageDirPaths
+ packageNames = fs.list(packageDirPath)
+ .filter((packagePath) -> fs.isDirectory(packagePath))
+ .map((packagePath) -> fs.base(packagePath))
+ allPackageNames.push(packageNames...)
+ _.unique(allPackageNames)
+
+ getAvailableTextMateBundles: ->
+ @getAvailablePackages().filter (packageName) => TextMatePackage.testName(packageName)
+
+ loadPackages: (packageNames=@getAvailablePackages()) ->
+ disabledPackages = config.get("core.disabledPackages") ? []
+ for packageName in packageNames
+ @loadPackage(packageName) unless _.contains(disabledPackages, packageName)
+
loadPackage: (name) ->
- try
- packagePath = require.resolve(name, verifyExistence: false)
- throw new Error("No package found named '#{name}'") unless packagePath
- packagePath = fs.directory(packagePath)
- extension = require(packagePath)
- extension.name = name
- rootView.activateExtension(extension)
- extensionKeymapPath = require.resolve(fs.join(name, "src/keymap"), verifyExistence: false)
- require extensionKeymapPath if fs.exists(extensionKeymapPath)
- catch e
- console.error "Failed to load package named '#{name}'", e.stack
+ Package.forName(name).load()
open: (args...) ->
@sendMessageToBrowserProcess('open', args)
@@ -85,4 +95,3 @@ _.extend atom,
if name is 'reply'
[messageId, callbackIndex] = data.shift()
@pendingBrowserProcessCallbacks[messageId]?[callbackIndex]?(data...)
-
diff --git a/src/app/config.coffee b/src/app/config.coffee
index f55987d46..0e3d0b035 100644
--- a/src/app/config.coffee
+++ b/src/app/config.coffee
@@ -1,11 +1,17 @@
fs = require 'fs'
_ = require 'underscore'
EventEmitter = require 'event-emitter'
+{$$} = require 'space-pen'
+jQuery = require 'jquery'
+Specificity = require 'specificity'
+Theme = require 'theme'
configDirPath = fs.absolute("~/.atom")
configJsonPath = fs.join(configDirPath, "config.json")
userInitScriptPath = fs.join(configDirPath, "atom.coffee")
+bundledThemesDirPath = fs.join(resourcePath, "themes")
bundledPackagesDirPath = fs.join(resourcePath, "src/packages")
+userThemesDirPath = fs.join(configDirPath, "themes")
userPackagesDirPath = fs.join(configDirPath, "packages")
require.paths.unshift userPackagesDirPath
@@ -13,46 +19,31 @@ require.paths.unshift userPackagesDirPath
module.exports =
class Config
configDirPath: configDirPath
+ themeDirPaths: [userThemesDirPath, bundledThemesDirPath]
+ packageDirPaths: [userPackagesDirPath, bundledPackagesDirPath]
settings: null
+ constructor: ->
+ @settings =
+ core: _.clone(require('root-view').configDefaults)
+ editor: _.clone(require('editor').configDefaults)
+
load: ->
- @settings = {}
@loadUserConfig()
- @assignDefaults()
- @loadPackages()
@requireUserInitScript()
+ atom.loadPackages()
+ Theme.load(config.get("core.theme") ? 'IR_Black')
loadUserConfig: ->
if fs.exists(configJsonPath)
userConfig = JSON.parse(fs.read(configJsonPath))
_.extend(@settings, userConfig)
- assignDefaults: ->
- @settings ?= {}
- @setDefaults "core", require('root-view').configDefaults
- @setDefaults "editor", require('editor').configDefaults
-
- getAvailablePackages: ->
- availablePackages =
- fs.list(bundledPackagesDirPath)
- .concat(fs.list(userPackagesDirPath)).map (path) -> fs.base(path)
- _.unique(availablePackages)
-
- loadPackages: ->
- disabledPackages = config.get("core.disabledPackages") ? []
- for packageName in @getAvailablePackages()
- unless _.contains disabledPackages, packageName
- atom.loadPackage(packageName)
-
get: (keyPath) ->
- keys = @keysForKeyPath(keyPath)
- value = @settings
- for key in keys
- break unless value = value[key]
- value
+ _.valueForKeyPath(@settings, keyPath)
set: (keyPath, value) ->
- keys = @keysForKeyPath(keyPath)
+ keys = keyPath.split('.')
hash = @settings
while keys.length > 1
key = keys.shift()
@@ -64,7 +55,7 @@ class Config
value
setDefaults: (keyPath, defaults) ->
- keys = @keysForKeyPath(keyPath)
+ keys = keyPath.split('.')
hash = @settings
for key in keys
hash[key] ?= {}
@@ -73,12 +64,6 @@ class Config
_.defaults hash, defaults
@update()
- keysForKeyPath: (keyPath) ->
- if typeof keyPath is 'string'
- keyPath.split(".")
- else
- new Array(keyPath...)
-
observe: (keyPath, callback) ->
value = @get(keyPath)
previousValue = _.clone(value)
diff --git a/src/app/keymap.coffee b/src/app/keymap.coffee
index 04ec03b8a..21710495a 100644
--- a/src/app/keymap.coffee
+++ b/src/app/keymap.coffee
@@ -3,7 +3,6 @@ _ = require 'underscore'
fs = require 'fs'
BindingSet = require 'binding-set'
-Specificity = require 'specificity'
module.exports =
class Keymap
diff --git a/src/app/language-mode.coffee b/src/app/language-mode.coffee
index 1305f9191..53fcfada7 100644
--- a/src/app/language-mode.coffee
+++ b/src/app/language-mode.coffee
@@ -67,14 +67,14 @@ class LanguageMode
toggleLineCommentsForBufferRows: (start, end) ->
scopes = @editSession.scopesForBufferPosition([start, 0])
- return unless commentStartString = TextMateBundle.lineCommentStartStringForScope(scopes[0])
+ return unless commentStartString = syntax.getProperty(scopes, "editor.commentStart")
buffer = @editSession.buffer
commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '($1)?')
commentStartRegex = new OnigRegExp("^(\\s*)(#{commentStartRegexString})")
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
- if commentEndString = TextMateBundle.lineCommentEndStringForScope(scopes[0])
+ if commentEndString = syntax.getProperty(scopes, "editor.commentEnd")
if shouldUncomment
commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '($1)?')
commentEndRegex = new OnigRegExp("(#{commentEndRegexString})(\\s*)$")
@@ -119,7 +119,7 @@ class LanguageMode
continue if @editSession.isBufferRowBlank(row)
indentation = @editSession.indentationForBufferRow(row)
if indentation <= startIndentLevel
- includeRowInFold = indentation == startIndentLevel and TextMateBundle.foldEndRegexForScope(@grammar, scopes[0]).search(@editSession.lineForBufferRow(row))
+ includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes).search(@editSession.lineForBufferRow(row))
foldEndRow = row if includeRowInFold
break
@@ -130,7 +130,7 @@ class LanguageMode
suggestedIndentForBufferRow: (bufferRow) ->
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
- return currentIndentLevel unless increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
+ return currentIndentLevel unless increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
currentLine = @buffer.lineForRow(bufferRow)
precedingRow = @buffer.previousNonBlankRow(bufferRow)
@@ -139,10 +139,10 @@ class LanguageMode
precedingLine = @buffer.lineForRow(precedingRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
- desiredIndentLevel += 1 if increaseIndentPattern.test(precedingLine)
+ desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine)
- return desiredIndentLevel unless decreaseIndentPattern = TextMateBundle.outdentRegexForScope(scopes[0])
- desiredIndentLevel -= 1 if decreaseIndentPattern.test(currentLine)
+ return desiredIndentLevel unless decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
+ desiredIndentLevel -= 1 if decreaseIndentRegex.test(currentLine)
Math.max(desiredIndentLevel, currentIndentLevel)
@@ -159,32 +159,44 @@ class LanguageMode
precedingLine = @editSession.lineForBufferRow(precedingRow)
scopes = @editSession.scopesForBufferPosition([precedingRow, Infinity])
- increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
- return unless increaseIndentPattern
+ increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
+ return unless increaseIndentRegex
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
- desiredIndentLevel += 1 if increaseIndentPattern.test(precedingLine)
+ desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine)
if desiredIndentLevel > currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
autoDecreaseIndentForBufferRow: (bufferRow) ->
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
- increaseIndentPattern = TextMateBundle.indentRegexForScope(scopes[0])
- decreaseIndentPattern = TextMateBundle.outdentRegexForScope(scopes[0])
- return unless increaseIndentPattern and decreaseIndentPattern
+ increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
+ decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
+ return unless increaseIndentRegex and decreaseIndentRegex
line = @buffer.lineForRow(bufferRow)
- return unless decreaseIndentPattern.test(line)
+ return unless decreaseIndentRegex.test(line)
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
precedingRow = @buffer.previousNonBlankRow(bufferRow)
precedingLine = @buffer.lineForRow(precedingRow)
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
- desiredIndentLevel -= 1 unless increaseIndentPattern.test(precedingLine)
+ desiredIndentLevel -= 1 unless increaseIndentRegex.test(precedingLine)
if desiredIndentLevel < currentIndentLevel
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
tokenizeLine: (line, stack, firstLine) ->
{tokens, stack} = @grammar.tokenizeLine(line, stack, firstLine)
+
+ increaseIndentRegexForScopes: (scopes) ->
+ if increaseIndentPattern = syntax.getProperty(scopes, 'editor.increaseIndentPattern')
+ new OnigRegExp(increaseIndentPattern)
+
+ decreaseIndentRegexForScopes: (scopes) ->
+ if decreaseIndentPattern = syntax.getProperty(scopes, 'editor.decreaseIndentPattern')
+ new OnigRegExp(decreaseIndentPattern)
+
+ foldEndRegexForScopes: (scopes) ->
+ if foldEndPattern = syntax.getProperty(scopes, 'editor.foldEndPattern')
+ new OnigRegExp(foldEndPattern)
diff --git a/src/app/package.coffee b/src/app/package.coffee
new file mode 100644
index 000000000..88fcf7ef1
--- /dev/null
+++ b/src/app/package.coffee
@@ -0,0 +1,21 @@
+fs = require 'fs'
+
+module.exports =
+class Package
+ @forName: (name) ->
+ AtomPackage = require 'atom-package'
+ TextMatePackage = require 'text-mate-package'
+
+ if TextMatePackage.testName(name)
+ new TextMatePackage(name)
+ else
+ new AtomPackage(name)
+
+ constructor: (@name) ->
+ @path = require.resolve(@name, verifyExistence: false)
+ throw new Error("No package found named '#{@name}'") unless @path
+ @path = fs.directory(@path) unless fs.isDirectory(@path)
+
+ load: ->
+ for { selector, properties } in @getScopedProperties()
+ syntax.addProperties(selector, properties)
diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee
index f25e97739..2961ef76b 100644
--- a/src/app/root-view.coffee
+++ b/src/app/root-view.coffee
@@ -10,7 +10,6 @@ Project = require 'project'
Pane = require 'pane'
PaneColumn = require 'pane-column'
PaneRow = require 'pane-row'
-TextMateTheme = require 'text-mate-theme'
module.exports =
class RootView extends View
@@ -24,25 +23,23 @@ class RootView extends View
@div id: 'vertical', outlet: 'vertical', =>
@div id: 'panes', outlet: 'panes'
- @deserialize: ({ projectPath, panesViewState, extensionStates }) ->
- rootView = new RootView(projectPath, extensionStates: extensionStates, suppressOpen: true)
+ @deserialize: ({ projectPath, panesViewState, packageStates }) ->
+ rootView = new RootView(projectPath, packageStates: packageStates, suppressOpen: true)
rootView.setRootPane(rootView.deserializeView(panesViewState)) if panesViewState
rootView
- extensions: null
- extensionStates: null
+ packageModules: null
+ packageStates: null
title: null
- initialize: (pathToOpen, { @extensionStates, suppressOpen } = {}) ->
+ initialize: (pathToOpen, { @packageStates, suppressOpen } = {}) ->
window.rootView = this
- @extensionStates ?= {}
- @extensions = {}
+ @packageStates ?= {}
+ @packageModules = {}
@project = new Project(pathToOpen)
config.load()
- TextMateTheme.activate(config.get("core.theme") ? 'IR_Black')
-
@handleEvents()
if pathToOpen
@@ -53,7 +50,7 @@ class RootView extends View
serialize: ->
projectPath: @project?.getPath()
panesViewState: @panes.children().view()?.serialize()
- extensionStates: @serializeExtensions()
+ packageStates: @serializePackages()
handleFocus: (e) ->
if @getActiveEditor()
@@ -99,14 +96,14 @@ class RootView extends View
afterAttach: (onDom) ->
@focus() if onDom
- serializeExtensions: ->
- extensionStates = {}
- for name, extension of @extensions
+ serializePackages: ->
+ packageStates = {}
+ for name, packageModule of @packageModules
try
- extensionStates[name] = extension.serialize?()
+ packageStates[name] = packageModule.serialize?()
catch e
- console?.error("Exception serializing '#{name}' extension\n", e.stack)
- extensionStates
+ console?.error("Exception serializing '#{name}' package's module\n", e.stack)
+ packageStates
deserializeView: (viewState) ->
switch viewState.viewClass
@@ -115,18 +112,18 @@ class RootView extends View
when 'PaneColumn' then PaneColumn.deserialize(viewState, this)
when 'Editor' then Editor.deserialize(viewState, this)
- activateExtension: (extension, config) ->
- throw new Error("Trying to activate an extension with no name attribute") unless extension.name?
- @extensions[extension.name] = extension
- extension.activate(this, @extensionStates[extension.name], config)
+ activatePackage: (packageModule) ->
+ throw new Error("Trying to activate a package module with no name attribute") unless packageModule.name?
+ @packageModules[packageModule.name] = packageModule
+ packageModule.activate(this, @packageStates[packageModule.name])
- deactivateExtension: (extension) ->
- extension.deactivate?()
- delete @extensions[extension.name]
+ deactivatePackage: (packageModule) ->
+ packageModule.deactivate?()
+ delete @packageModules[packageModule.name]
deactivate: ->
atom.setRootViewStateForPath(@project.getPath(), @serialize())
- @deactivateExtension(extension) for name, extension of @extensions
+ @deactivatePackage(packageModule) for name, packageModule of @packageModules
@remove()
open: (path, options = {}) ->
diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee
new file mode 100644
index 000000000..5a71da15e
--- /dev/null
+++ b/src/app/syntax.coffee
@@ -0,0 +1,70 @@
+_ = require 'underscore'
+jQuery = require 'jquery'
+Specificity = require 'specificity'
+{$$} = require 'space-pen'
+
+module.exports =
+class Syntax
+ constructor: ->
+ @globalProperties = {}
+ @scopedPropertiesIndex = 0
+ @scopedProperties = []
+ @propertiesBySelector = {}
+
+ addProperties: (args...) ->
+ selector = args.shift() if args.length > 1
+ properties = args.shift()
+
+ if selector
+ @scopedProperties.unshift(
+ selector: selector,
+ properties: properties,
+ specificity: Specificity(selector),
+ index: @scopedPropertiesIndex++
+ )
+ else
+ _.extend(@globalProperties, properties)
+
+ getProperty: (scope, keyPath) ->
+ for object in @propertiesForScope(scope, keyPath)
+ value = _.valueForKeyPath(object, keyPath)
+ return value if value?
+ undefined
+
+ propertiesForScope: (scope, keyPath) ->
+ matchingProperties = []
+ candidates = @scopedProperties.filter ({properties}) -> _.valueForKeyPath(properties, keyPath)?
+ if candidates.length
+ element = @buildScopeElement(scope)
+ while element
+ matchingProperties.push(@matchingPropertiesForElement(element, candidates)...)
+ element = element.parentNode
+ matchingProperties.concat([@globalProperties])
+
+ matchingPropertiesForElement: (element, candidates) ->
+ matchingScopedProperties = candidates.filter ({selector}) ->
+ jQuery.find.matchesSelector(element, selector)
+ matchingScopedProperties.sort (a, b) ->
+ if a.specificity == b.specificity
+ b.index - a.index
+ else
+ b.specificity - a.specificity
+ _.pluck matchingScopedProperties, 'properties'
+
+ buildScopeElement: (scope) ->
+ scope = new Array(scope...)
+ element = $$ ->
+ elementsForRemainingScopes = =>
+ classString = scope.shift()
+ classes = classString.replace(/^\./, '').replace(/\./g, ' ')
+ if scope.length
+ @div class: classes, elementsForRemainingScopes
+ else
+ @div class: classes
+ elementsForRemainingScopes()
+
+ deepestChild = element.find(":not(:has(*))")
+ if deepestChild.length
+ deepestChild[0]
+ else
+ element[0]
diff --git a/src/app/text-mate-bundle.coffee b/src/app/text-mate-bundle.coffee
index 6ead4ccff..5d31499eb 100644
--- a/src/app/text-mate-bundle.coffee
+++ b/src/app/text-mate-bundle.coffee
@@ -10,18 +10,10 @@ class TextMateBundle
@grammarsByFileType: {}
@grammarsByScopeName: {}
@preferencesByScopeSelector: {}
- @bundles: []
@grammars: []
- @loadAll: ->
- localBundlePath = fs.join(config.configDirPath, "bundles")
- localBundles = fs.list(localBundlePath) if fs.exists(localBundlePath)
-
- for bundlePath in localBundles ? []
- @registerBundle(new TextMateBundle(bundlePath))
-
- @registerBundle: (bundle)->
- @bundles.push(bundle)
+ @load: (name)->
+ bundle = new TextMateBundle(require.resolve(name))
for scopeSelector, preferences of bundle.getPreferencesByScopeSelector()
if @preferencesByScopeSelector[scopeSelector]?
@@ -35,6 +27,8 @@ class TextMateBundle
@grammarsByFileType[fileType] = grammar
@grammarsByScopeName[grammar.scopeName] = grammar
+ bundle
+
@grammarForFilePath: (filePath) ->
return @grammarsByFileType["txt"] unless filePath
@@ -59,34 +53,6 @@ class TextMateBundle
@grammarForScopeName: (scopeName) ->
@grammarsByScopeName[scopeName]
- @getPreferenceInScope: (scopeSelector, preferenceName) ->
- @preferencesByScopeSelector[scopeSelector]?[preferenceName]
-
- @getPreferenceValueInScope: (scope, preferenceName, valueName) ->
- values = @getPreferenceInScope(scope, preferenceName)
- (_.find values, ({name}) -> name is valueName)?['value']
-
- @lineCommentStartStringForScope: (scope) ->
- @getPreferenceValueInScope(scope, 'shellVariables', 'TM_COMMENT_START')
-
- @lineCommentEndStringForScope: (scope) ->
- @getPreferenceValueInScope(scope, 'shellVariables', 'TM_COMMENT_END')
-
- @indentRegexForScope: (scope) ->
- if source = @getPreferenceInScope(scope, 'increaseIndentPattern')
- new OnigRegExp(source)
-
- @outdentRegexForScope: (scope) ->
- if source = @getPreferenceInScope(scope, 'decreaseIndentPattern')
- new OnigRegExp(source)
-
- @foldEndRegexForScope: (grammar, scope) ->
- marker = @getPreferenceInScope(scope, 'foldingStopMarker')
- if marker
- new OnigRegExp(marker)
- else
- new OnigRegExp(grammar.foldingStopMarker)
-
grammars: null
constructor: (@path) ->
diff --git a/src/app/text-mate-package.coffee b/src/app/text-mate-package.coffee
new file mode 100644
index 000000000..c28c63937
--- /dev/null
+++ b/src/app/text-mate-package.coffee
@@ -0,0 +1,74 @@
+Package = require 'package'
+TextMateBundle = require 'text-mate-bundle'
+fs = require 'fs'
+plist = require 'plist'
+_ = require 'underscore'
+
+module.exports =
+class TextMatePackage extends Package
+ @testName: (packageName) ->
+ /(\.|_|-)tmbundle$/.test(packageName)
+
+ @cssSelectorFromScopeSelector: (scopeSelector) ->
+ scopeSelector.split(', ').map((commaFragment) ->
+ commaFragment.split(' ').map((spaceFragment) ->
+ spaceFragment.split('.').map((dotFragment) ->
+ '.' + dotFragment.replace(/\+/g, '\\+')
+ ).join('')
+ ).join(' ')
+ ).join(', ')
+
+ load: ->
+ @bundle = TextMateBundle.load(@name)
+ @grammars = @bundle.grammars
+ super
+
+ constructor: ->
+ super
+ @preferencesPath = fs.join(@path, "Preferences")
+ @syntaxesPath = fs.join(@path, "Syntaxes")
+
+ getScopedProperties: ->
+ scopedProperties = []
+
+ for grammar in @grammars
+ if properties = @propertiesFromTextMateSettings(grammar)
+ selector = @cssSelectorFromScopeSelector(grammar.scopeName)
+ scopedProperties.push({selector, properties})
+
+ for {scope, settings} in @getTextMatePreferenceObjects()
+ if properties = @propertiesFromTextMateSettings(settings)
+ selector = @cssSelectorFromScopeSelector(scope) if scope?
+ scopedProperties.push({selector, properties})
+
+ scopedProperties
+
+ getTextMatePreferenceObjects: ->
+ preferenceObjects = []
+ if fs.exists(@preferencesPath)
+ for preferencePath in fs.list(@preferencesPath)
+ plist.parseString fs.read(preferencePath), (e, data) =>
+ if e
+ console.warn "Failed to parse preference at path '#{preferencePath}'", e.stack
+ else
+ preferenceObjects.push(data[0])
+ preferenceObjects
+
+ propertiesFromTextMateSettings: (textMateSettings) ->
+ if textMateSettings.shellVariables
+ shellVariables = {}
+ for {name, value} in textMateSettings.shellVariables
+ shellVariables[name] = value
+ textMateSettings.shellVariables = shellVariables
+
+ editorProperties = _.compactObject(
+ commentStart: _.valueForKeyPath(textMateSettings, 'shellVariables.TM_COMMENT_START')
+ commentEnd: _.valueForKeyPath(textMateSettings, 'shellVariables.TM_COMMENT_END')
+ increaseIndentPattern: textMateSettings.increaseIndentPattern
+ decreaseIndentPattern: textMateSettings.decreaseIndentPattern
+ foldEndPattern: textMateSettings.foldingStopMarker
+ )
+ { editor: editorProperties } if _.size(editorProperties) > 0
+
+ cssSelectorFromScopeSelector: (scopeSelector) ->
+ @constructor.cssSelectorFromScopeSelector(scopeSelector)
\ No newline at end of file
diff --git a/src/app/text-mate-theme.coffee b/src/app/text-mate-theme.coffee
index 70f0602e2..caae44298 100644
--- a/src/app/text-mate-theme.coffee
+++ b/src/app/text-mate-theme.coffee
@@ -1,46 +1,16 @@
_ = require 'underscore'
fs = require 'fs'
-plist = require 'plist'
+Theme = require 'theme'
module.exports =
-class TextMateTheme
- @themesByName: {}
-
- @loadAll: ->
- for themePath in fs.list(require.resolve("themes"))
- @registerTheme(TextMateTheme.load(themePath))
-
- @load: (path) ->
- plistString = fs.read(require.resolve(path))
- theme = null
- plist.parseString plistString, (err, data) ->
- throw new Error("Error loading theme at '#{path}': #{err}") if err
- theme = new TextMateTheme(data[0])
- theme
-
- @registerTheme: (theme) ->
- @themesByName[theme.name] = theme
-
- @getNames: ->
- _.keys(@themesByName)
-
- @getTheme: (name) ->
- @themesByName[name]
-
- @activate: (name) ->
- if theme = @getTheme(name)
- theme.activate()
- else
- throw new Error("No theme with name '#{name}'")
-
- constructor: ({@name, settings}) ->
+class TextMateTheme extends Theme
+ constructor: (@path, {settings}) ->
+ super
@rulesets = []
globalSettings = settings[0]
@buildGlobalSettingsRulesets(settings[0])
@buildScopeSelectorRulesets(settings[1..])
-
- activate: ->
- applyStylesheet(@name, @getStylesheet())
+ @stylesheets[@path] = @getStylesheet()
getStylesheet: ->
lines = []
diff --git a/src/app/theme.coffee b/src/app/theme.coffee
new file mode 100644
index 000000000..1f8fea54e
--- /dev/null
+++ b/src/app/theme.coffee
@@ -0,0 +1,56 @@
+fs = require("fs")
+plist = require 'plist'
+_ = require 'underscore'
+
+module.exports =
+class Theme
+ @stylesheets: null
+
+ @load: (names) ->
+ if typeof(names) == "string"
+ [@loadTheme(names)]
+ else
+ names.map (name) => @loadTheme(name)
+
+ @loadTheme: (name) ->
+ if fs.exists(name)
+ path = name
+ else
+ path = fs.resolve(config.themeDirPaths..., name)
+ path ?= fs.resolve(config.themeDirPaths..., name + ".tmTheme")
+
+ if @isTextMateTheme(path)
+ theme = @loadTextMateTheme(path)
+ else
+ theme = @loadAtomTheme(path)
+
+ throw new Error("Cannot activate theme named '#{name}' located at '#{path}'") unless theme
+ theme.activate()
+ theme
+
+ @loadTextMateTheme: (path) ->
+ TextMateTheme = require("text-mate-theme")
+ plistString = fs.read(path)
+ theme = null
+ plist.parseString plistString, (err, data) ->
+ throw new Error("Error loading theme at '#{path}': #{err}") if err
+ theme = new TextMateTheme(path, data[0])
+ theme
+
+ @loadAtomTheme: (path) ->
+ AtomTheme = require('atom-theme')
+ new AtomTheme(path)
+
+ @isTextMateTheme: (path) ->
+ /\.(tmTheme|plist)$/.test(path)
+
+ constructor: (@path) ->
+ @stylesheets = {}
+
+ activate: ->
+ for stylesheetPath, stylesheetContent of @stylesheets
+ applyStylesheet(stylesheetPath, stylesheetContent)
+
+ deactivate: ->
+ for stylesheetPath, stylesheetContent of @stylesheets
+ window.removeStylesheet(stylesheetPath)
\ No newline at end of file
diff --git a/src/app/window.coffee b/src/app/window.coffee
index 16c55e5d4..5eccba916 100644
--- a/src/app/window.coffee
+++ b/src/app/window.coffee
@@ -9,6 +9,7 @@ _ = require 'underscore'
$ = require 'jquery'
{CoffeeScript} = require 'coffee-script'
Config = require 'config'
+Syntax = require 'syntax'
RootView = require 'root-view'
Pasteboard = require 'pasteboard'
require 'jquery-extensions'
@@ -25,8 +26,7 @@ windowAdditions =
# in all environments: spec, benchmark, and application
startup: ->
@config = new Config
- TextMateBundle.loadAll()
- TextMateTheme.loadAll()
+ @syntax = new Syntax
@setUpKeymap()
@pasteboard = new Pasteboard
@@ -65,9 +65,14 @@ windowAdditions =
requireStylesheet: (path) ->
unless fullPath = require.resolve(path)
- throw new Error("requireStylesheet could not find a file at path '#{path}'")
+ throw new Error("Could not find a file at path '#{path}'")
window.applyStylesheet(fullPath, fs.read(fullPath))
+ removeStylesheet: (path) ->
+ unless fullPath = require.resolve(path)
+ throw new Error("Could not find a file at path '#{path}'")
+ $("head style[id='#{fullPath}']").remove()
+
applyStylesheet: (id, text) ->
unless $("head style[id='#{id}']").length
$('head').append ""
diff --git a/src/packages/command-panel/spec/command-panel-spec.coffee b/src/packages/command-panel/spec/command-panel-spec.coffee
index 653642a1d..8beaed7ad 100644
--- a/src/packages/command-panel/spec/command-panel-spec.coffee
+++ b/src/packages/command-panel/spec/command-panel-spec.coffee
@@ -36,7 +36,7 @@ describe "CommandPanel", ->
rootView.deactivate()
rootView2.attachToDom()
- commandPanel = rootView2.activateExtension(CommandPanel)
+ commandPanel = rootView2.activatePackage(CommandPanel)
expect(rootView2.find('.command-panel')).toExist()
expect(commandPanel.miniEditor.getText()).toBe 'abc'
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
@@ -49,7 +49,7 @@ describe "CommandPanel", ->
rootView3 = RootView.deserialize(rootView2.serialize())
rootView2.deactivate()
rootView3.attachToDom()
- commandPanel = rootView3.activateExtension(CommandPanel)
+ commandPanel = rootView3.activatePackage(CommandPanel)
expect(commandPanel.miniEditor.isFocused).toBeFalsy()
rootView3.deactivate()
@@ -71,7 +71,7 @@ describe "CommandPanel", ->
rootView.deactivate()
rootView2.attachToDom()
- commandPanel = rootView2.activateExtension(CommandPanel)
+ commandPanel = rootView2.activatePackage(CommandPanel)
expect(commandPanel.history.length).toBe(2)
expect(commandPanel.history[0]).toBe('/test2')
expect(commandPanel.history[1]).toBe('/test3')
diff --git a/src/packages/event-palette/spec/event-palette-spec.coffee b/src/packages/event-palette/spec/event-palette-spec.coffee
index dbb419215..53e8448ee 100644
--- a/src/packages/event-palette/spec/event-palette-spec.coffee
+++ b/src/packages/event-palette/spec/event-palette-spec.coffee
@@ -8,7 +8,7 @@ describe "EventPalette", ->
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
- rootView.activateExtension(EventPalette)
+ atom.loadPackage("event-palette")
palette = EventPalette.instance
rootView.attachToDom().focus()
rootView.trigger 'event-palette:toggle'
diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee
index 0c7d7e507..1306dcf1f 100644
--- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee
+++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee
@@ -10,7 +10,7 @@ describe 'FuzzyFinder', ->
beforeEach ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
rootView.enableKeymap()
- rootView.activateExtension(FuzzyFinder)
+ atom.loadPackage("fuzzy-finder")
finder = FuzzyFinder.instance
afterEach ->
diff --git a/src/packages/fuzzy-finder/src/fuzzy-finder.coffee b/src/packages/fuzzy-finder/src/fuzzy-finder.coffee
index cf6bc4562..d51871207 100644
--- a/src/packages/fuzzy-finder/src/fuzzy-finder.coffee
+++ b/src/packages/fuzzy-finder/src/fuzzy-finder.coffee
@@ -73,7 +73,8 @@ class FuzzyFinder extends SelectList
else
@setLoading("Indexing...")
@rootView.project.getFilePaths().done (paths) =>
- ignoredNames = config.get("fuzzy-finder.ignoredNames")
+ ignoredNames = config.get("fuzzyFinder.ignoredNames") or []
+ ignoredNames = ignoredNames.concat(config.get("core.ignoredNames") or [])
@projectPaths = paths
if ignoredNames
@projectPaths = @projectPaths.filter (path) ->
diff --git a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee
index 994e03884..55a364261 100644
--- a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee
+++ b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee
@@ -7,7 +7,7 @@ describe "MarkdownPreview", ->
beforeEach ->
rootView = new RootView(require.resolve('fixtures/markdown'))
- rootView.activateExtension(MarkdownPreview)
+ atom.loadPackage("markdown-preview")
markdownPreview = MarkdownPreview.instance
rootView.attachToDom()
diff --git a/src/packages/outline-view/spec/outline-view-spec.coffee b/src/packages/outline-view/spec/outline-view-spec.coffee
index e133e86c5..e74981663 100644
--- a/src/packages/outline-view/spec/outline-view-spec.coffee
+++ b/src/packages/outline-view/spec/outline-view-spec.coffee
@@ -7,7 +7,7 @@ describe "OutlineView", ->
beforeEach ->
rootView = new RootView(require.resolve('fixtures'))
- rootView.activateExtension(OutlineView)
+ atom.loadPackage("outline-view")
outlineView = OutlineView.instance
rootView.attachToDom()
setArraySpy = spyOn(outlineView, 'setArray').andCallThrough()
diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee
index 4df375f29..c002ba04d 100644
--- a/src/packages/tabs/spec/tabs-spec.coffee
+++ b/src/packages/tabs/spec/tabs-spec.coffee
@@ -11,7 +11,7 @@ describe "Tabs", ->
rootView = new RootView(require.resolve('fixtures/sample.js'))
rootView.open('sample.txt')
rootView.simulateDomAttachment()
- rootView.activateExtension(Tabs)
+ atom.loadPackage("tabs")
editor = rootView.getActiveEditor()
tabs = rootView.find('.tabs').view()
diff --git a/src/packages/tree-view/spec/tree-view-spec.coffee b/src/packages/tree-view/spec/tree-view-spec.coffee
index 1923e8ada..7de459451 100644
--- a/src/packages/tree-view/spec/tree-view-spec.coffee
+++ b/src/packages/tree-view/spec/tree-view-spec.coffee
@@ -13,7 +13,7 @@ describe "TreeView", ->
rootView = new RootView(require.resolve('fixtures/tree-view'))
project = rootView.project
- rootView.activateExtension(TreeView)
+ atom.loadPackage("tree-view")
treeView = rootView.find(".tree-view").view()
treeView.root = treeView.find('> li:first').view()
sampleJs = treeView.find('.file:contains(tree-view.js)')
@@ -51,7 +51,7 @@ describe "TreeView", ->
rootView.deactivate()
rootView = new RootView
- rootView.activateExtension(TreeView)
+ rootView.activatePackage(TreeView)
treeView = rootView.find(".tree-view").view()
it "does not create a root node", ->
@@ -74,7 +74,7 @@ describe "TreeView", ->
describe "when the prototypes deactivate method is called", ->
it "calls the deactivate on tree view instance", ->
spyOn(treeView, "deactivate").andCallThrough()
- rootView.deactivateExtension(TreeView)
+ rootView.deactivatePackage(TreeView)
expect(treeView.deactivate).toHaveBeenCalled()
describe "serialization", ->
@@ -89,7 +89,7 @@ describe "TreeView", ->
newRootView = RootView.deserialize(rootView.serialize())
rootView.deactivate() # Deactivates previous TreeView
- newRootView.activateExtension(TreeView)
+ newRootView.activatePackage(TreeView)
newTreeView = newRootView.find(".tree-view").view()
@@ -106,7 +106,7 @@ describe "TreeView", ->
rootView.deactivate() # Deactivates previous TreeView
newRootView.attachToDom()
- newRootView.activateExtension(TreeView)
+ newRootView.activatePackage(TreeView)
newTreeView = newRootView.find(".tree-view").view()
expect(newTreeView).toMatchSelector ':focus'
@@ -589,7 +589,7 @@ describe "TreeView", ->
rootView = new RootView(rootDirPath)
project = rootView.project
- rootView.activateExtension(TreeView)
+ rootView.activatePackage(TreeView)
treeView = rootView.find(".tree-view").view()
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()
dirView.expand()
diff --git a/src/stdlib/fs.coffee b/src/stdlib/fs.coffee
index 9b9e97cda..b4470d916 100644
--- a/src/stdlib/fs.coffee
+++ b/src/stdlib/fs.coffee
@@ -124,6 +124,13 @@ module.exports =
md5ForPath: (path) ->
$native.md5ForPath(path)
+ resolve: (paths...) ->
+ to = paths.pop()
+ for from in paths
+ path = @join(from, to)
+ return path if @exists(path)
+ undefined
+
isCompressedExtension: (ext) ->
_.contains([
'.gz'
diff --git a/src/stdlib/underscore-extensions.coffee b/src/stdlib/underscore-extensions.coffee
index c1e6252a6..4946bcdea 100644
--- a/src/stdlib/underscore-extensions.coffee
+++ b/src/stdlib/underscore-extensions.coffee
@@ -85,3 +85,16 @@ _.mixin
endsWith: (string, suffix) ->
string.indexOf(suffix, string.length - suffix.length) isnt -1
+
+ valueForKeyPath: (object, keyPath) ->
+ keys = keyPath.split('.')
+ for key in keys
+ object = object[key]
+ return unless object?
+ object
+
+ compactObject: (object) ->
+ newObject = {}
+ for key, value of object
+ newObject[key] = value if value?
+ newObject