diff --git a/.atom/config.json b/.atom/config.json new file mode 100644 index 000000000..50770e810 --- /dev/null +++ b/.atom/config.json @@ -0,0 +1,11 @@ +{ + "editor": { + "fontSize": 16 + }, + "core": { + "themes": [ + "atom-dark-ui", + "atom-dark-syntax" + ] + } +} diff --git a/.atom/packages/Readme.md b/.atom/packages/Readme.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/.atom/themes/README.md b/.atom/themes/README.md new file mode 100644 index 000000000..dbedd5e13 --- /dev/null +++ b/.atom/themes/README.md @@ -0,0 +1 @@ +All themes in this directory will be automatically loaded diff --git a/.atom/atom.coffee b/.atom/user.coffee similarity index 100% rename from .atom/atom.coffee rename to .atom/user.coffee diff --git a/.atom/user.css b/.atom/user.css new file mode 100644 index 000000000..d63ad257e --- /dev/null +++ b/.atom/user.css @@ -0,0 +1,8 @@ +/* User styles */ +.tree-view { + +} + +.editor { + +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac09dc3e8..0c48073b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,8 @@ styleguides * Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/) specs + * Style new elements in both the light and dark default themes when + appropriate * New packages go in `src/packages/` * Add 3rd-party packages by submoduling in `vendor/packages/` * Commit messages are in the present tense diff --git a/README.md b/README.md index f6e23611e..04459faa3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Requirements Atom is installed! Type `atom [path]` from the commmand line or find it in `/Applications/Atom.app` ## Your ~/.atom Directory -A basic ~/.atom directory is installed when you run `rake install`. Take a look at ~/.atom/atom.coffee for more information. +A basic ~/.atom directory is installed when you run `rake install`. Take a look at ~/.atom/user.coffee for more information. ## Basic Keyboard shortcuts Atom doesn't have much in the way of menus yet. Use these keyboard shortcuts to @@ -53,7 +53,7 @@ Most default OS X keybindings also work. ## Init Script -Atom will require `~/.atom/atom.coffee` whenever a window is opened or reloaded if it is present in your +Atom will require `~/.atom/user.coffee` whenever a window is opened or reloaded if it is present in your home directory. This is a rudimentary jumping off point for your own customizations. ## Command Panel diff --git a/Rakefile b/Rakefile index e6adbba94..e0dd5abd1 100644 --- a/Rakefile +++ b/Rakefile @@ -76,14 +76,26 @@ task "create-dot-atom" do dot_atom_template_path = ATOM_SRC_PATH + "/.atom" replace_dot_atom = false - next if File.exists?(DOT_ATOM_PATH) + + if File.exists?(DOT_ATOM_PATH) + user_config = "#{DOT_ATOM_PATH}/user.coffee" + old_user_config = "#{DOT_ATOM_PATH}/atom.coffee" + + if File.exists?(old_user_config) + `mv #{old_user_config} #{user_config}` + puts "\033[32mRenamed #{old_user_config} to #{user_config}\033[0m" + end + + next + end `rm -rf "#{DOT_ATOM_PATH}"` `mkdir "#{DOT_ATOM_PATH}"` - `cp "#{dot_atom_template_path}/atom.coffee" "#{DOT_ATOM_PATH}"` - `cp "#{dot_atom_template_path}/packages" "#{DOT_ATOM_PATH}"` + + `cp "#{dot_atom_template_path}/user.coffee" "#{DOT_ATOM_PATH}"` + `cp "#{dot_atom_template_path}/user.css" "#{DOT_ATOM_PATH}"` + `cp -r "#{dot_atom_template_path}/packages" "#{DOT_ATOM_PATH}"` `cp -r "#{ATOM_SRC_PATH}/themes" "#{DOT_ATOM_PATH}"` - `cp "#{ATOM_SRC_PATH}/vendor/themes/IR_Black.tmTheme" "#{DOT_ATOM_PATH}/themes"` end desc "Clone default bundles into vendor/bundles directory" diff --git a/docs/styling.md b/docs/styling.md index 1632c4e54..847df3fbb 100644 --- a/docs/styling.md +++ b/docs/styling.md @@ -9,8 +9,8 @@ gutter. You can change the background color using the following CSS: ```css -.editor.focused .line.cursor-line, -.editor.focused .line-number.cursor-line { +.editor.is-focused .line.cursor-line, +.editor.is-focused .line-number.cursor-line { background-color: green; } ``` @@ -18,7 +18,7 @@ You can change the background color using the following CSS: You can change the line number foreground color using the following CSS: ```css -.editor.focused .line-number.cursor-line { +.editor.is-focused .line-number.cursor-line { color: blue; } ``` diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index a02c84d97..73474a2a1 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -26,17 +26,20 @@ describe "the `atom` global", -> describe "keymap loading", -> describe "when package.json does not contain a 'keymaps' manifest", -> - it "loads all keymaps in the directory", -> + it "loads all the .cson/.json files in the keymaps directory", -> element1 = $$ -> @div class: 'test-1' element2 = $$ -> @div class: 'test-2' + element3 = $$ -> @div class: 'test-3' expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined() + expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() atom.loadPackage("package-with-module") expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1" expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2" + expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() describe "when package.json contains a 'keymaps' manifest", -> it "loads only the keymaps specified by the manifest, in the specified order", -> diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index 9304d9df0..8bfb4a2f6 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -111,11 +111,11 @@ describe "Editor", -> editor.isFocused = false editor.hiddenInput.focus() expect(editor.isFocused).toBeTruthy() - expect(editor).toHaveClass('focused') + expect(editor).toHaveClass('is-focused') editor.hiddenInput.focusout() expect(editor.isFocused).toBeFalsy() - expect(editor).not.toHaveClass('focused') + expect(editor).not.toHaveClass('is-focused') describe "when the activeEditSession's file is modified on disk", -> it "triggers an alert", -> @@ -520,9 +520,8 @@ describe "Editor", -> beforeEach -> expect(editor.css('font-family')).not.toBe 'Courier' - it "sets the initial font family based on the value from config", -> - expect($("head style.font-family")).toExist() - expect($("head style.font-family").text()).toMatch "{font-family: #{config.get('editor.fontFamily')}}" + it "when there is no config in fontFamily don't set it", -> + expect($("head style.font-family")).not.toExist() describe "when the font family changes", -> it "updates the font family on new and existing editors", -> @@ -544,7 +543,7 @@ describe "Editor", -> lineHeightBefore = editor.lineHeight charWidthBefore = editor.charWidth - config.set("editor.fontFamily", "Courier") + config.set("editor.fontFamily", "Inconsolata") editor.setCursorScreenPosition [5, 6] expect(editor.charWidth).not.toBe charWidthBefore expect(editor.getCursorView().position()).toEqual { top: 5 * editor.lineHeight, left: 6 * editor.charWidth } @@ -553,6 +552,7 @@ describe "Editor", -> describe "font size", -> beforeEach -> expect(editor.css('font-size')).not.toBe "20px" + expect(editor.css('font-size')).not.toBe "10px" it "sets the initial font size based on the value from config", -> expect($("head style.font-size")).toExist() @@ -615,6 +615,23 @@ describe "Editor", -> config.set("editor.fontSize", 10) expect(editor.renderedLines.find(".line").length).toBeGreaterThan originalLineCount + describe "when the editor is detached", -> + it "updates the font-size correctly and recalculates the dimensions by placing the rendered lines on the DOM", -> + rootView.attachToDom() + rootView.height(200) + rootView.width(200) + + newEditor = editor.splitRight() + newEditorParent = newEditor.parent() + newEditor.detach() + config.set("editor.fontSize", 10) + newEditorParent.append(newEditor) + + expect(newEditor.lineHeight).toBe editor.lineHeight + expect(newEditor.charWidth).toBe editor.charWidth + expect(newEditor.getCursorView().position()).toEqual editor.getCursorView().position() + expect(newEditor.verticalScrollbarContent.height()).toBe editor.verticalScrollbarContent.height() + describe "mouse events", -> beforeEach -> editor.attachToDom() diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee index d15eb5c74..9660fcecb 100644 --- a/spec/app/syntax-spec.coffee +++ b/spec/app/syntax-spec.coffee @@ -23,8 +23,7 @@ describe "the `syntax` global", -> expect(syntax.grammarForFilePath("/tmp/.git/config").name).toBe "Git Config" it "uses plain text if no grammar can be found", -> - filePath = require.resolve("this-is-not-a-real-file") - expect(syntax.grammarForFilePath(filePath).name).toBe "Plain Text" + expect(syntax.grammarForFilePath("this-is-not-a-real-file").name).toBe "Plain Text" describe ".getProperty(scopeDescriptor)", -> it "returns the property with the most specific scope selector", -> diff --git a/spec/app/text-mate-theme-spec.coffee b/spec/app/text-mate-theme-spec.coffee index 5191ced55..e1c04ec90 100644 --- a/spec/app/text-mate-theme-spec.coffee +++ b/spec/app/text-mate-theme-spec.coffee @@ -42,7 +42,7 @@ describe "TextMateTheme", -> selector: ".invalid.deprecated" properties: 'color': "#D2A8A1" - # 'font-style': 'italic' + 'font-style': 'italic' 'text-decoration': 'underline' expect(rulesets[13]).toEqual diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index 2471d6cc3..c8a61920b 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -13,6 +13,14 @@ describe "Window", -> atom.setRootViewStateForPath(rootView.project.getPath(), null) $(window).off 'beforeunload' + describe "window is loaded", -> + it "has .is-focused on the body tag", -> + expect($("body").hasClass("is-focused")).toBe true + + it "doesn't have .is-focused on the window blur", -> + $(window).blur() + expect($("body").hasClass("is-focused")).toBe false + describe ".close()", -> it "is triggered by the 'core:close' event", -> spyOn window, 'close' diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-3.cjson b/spec/fixtures/packages/package-with-module/keymaps/keymap-3.cjson new file mode 100644 index 000000000..3c95c85a5 --- /dev/null +++ b/spec/fixtures/packages/package-with-module/keymaps/keymap-3.cjson @@ -0,0 +1,2 @@ +".test-3": + "ctrl-z": "test-3" diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index a0e49740a..1a4657488 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -85,7 +85,7 @@ jasmine.unspy = (object, methodName) -> throw new Error("Not a spy") unless object[methodName].originalValue? object[methodName] = object[methodName].originalValue -jasmine.getEnv().defaultTimeoutInterval = 500 +jasmine.getEnv().defaultTimeoutInterval = 1000 window.keyIdentifierForKey = (key) -> if key.length > 1 # named key diff --git a/spec/stdlib/cson-spec.coffee b/spec/stdlib/cson-spec.coffee new file mode 100644 index 000000000..d14ef3467 --- /dev/null +++ b/spec/stdlib/cson-spec.coffee @@ -0,0 +1,60 @@ +CSON = require 'cson' + +describe "CSON", -> + describe "@stringify(object)", -> + describe "when the object is undefined", -> + it "throws an exception", -> + expect(-> CSON.stringify()).toThrow() + + describe "when the object is a function", -> + it "throws an exception", -> + expect(-> CSON.stringify(-> 'function')).toThrow() + + describe "when the object contains a function", -> + it "throws an exception", -> + expect(-> CSON.stringify(a: -> 'function')).toThrow() + + describe "when formatting an undefined key", -> + it "does not include the key in the formatted CSON", -> + expect(CSON.stringify(b: 1, c: undefined)).toBe "'b': 1" + + describe "when formatting an empty object", -> + it "returns the empty string", -> + expect(CSON.stringify({})).toBe "" + + describe "when formatting a string", -> + it "returns formatted CSON", -> + expect(CSON.stringify(a: 'b')).toBe "'a': 'b'" + + it "escapes single quotes", -> + expect(CSON.stringify(a: "'b'")).toBe "'a': '\\\'b\\\''" + + it "doesn't escape double quotes", -> + expect(CSON.stringify(a: '"b"')).toBe "'a': '\"b\"'" + + describe "when formatting a boolean", -> + it "returns formatted CSON", -> + expect(CSON.stringify(a: true)).toBe "'a': true" + expect(CSON.stringify(a: false)).toBe "'a': false" + + describe "when formatting a number", -> + it "returns formatted CSON", -> + expect(CSON.stringify(a: 14)).toBe "'a': 14" + expect(CSON.stringify(a: 1.23)).toBe "'a': 1.23" + + describe "when formatting null", -> + it "returns formatted CSON", -> + expect(CSON.stringify(a: null)).toBe "'a': null" + + describe "when formatting an array", -> + describe "when the array is empty", -> + it "puts the array on a single line", -> + expect(CSON.stringify([])).toBe "[]" + + it "returns formatted CSON", -> + expect(CSON.stringify(a: ['b'])).toBe "'a': [\n 'b'\n]" + expect(CSON.stringify(a: ['b', 4])).toBe "'a': [\n 'b'\n 4\n]" + + describe "when formatting an object", -> + it "returns formatted CSON", -> + expect(CSON.stringify(a: {b: 'c'})).toBe "'a':\n 'b': 'c'" diff --git a/spec/stdlib/fs-spec.coffee b/spec/stdlib/fs-spec.coffee index ffcdaf31b..b0a6fae5f 100644 --- a/spec/stdlib/fs-spec.coffee +++ b/spec/stdlib/fs-spec.coffee @@ -128,3 +128,8 @@ describe "fs", -> describe ".md5ForPath(path)", -> it "returns the MD5 hash of the file at the given path", -> expect(fs.md5ForPath(require.resolve('fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b' + + describe ".list(path, extensions)", -> + it "returns the paths with the specified extensions", -> + path = require.resolve('fixtures/css.css') + expect(fs.list(require.resolve('fixtures'), ['.css'])).toEqual [path] diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index bb1ce071b..15a4e465a 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -26,15 +26,12 @@ class AtomPackage extends Package @metadata = fs.readObject(metadataPath) loadKeymaps: -> - for keymapPath in @getKeymapPaths() - keymap.load(keymapPath) - - getKeymapPaths: -> if keymaps = @metadata?.keymaps - keymaps.map (relativePath) => + keymaps = keymaps.map (relativePath) => fs.resolve(@keymapsDirPath, relativePath, ['cson', 'json', '']) + keymap.load(keymapPath) for keymapPath in keymaps else - fs.list(@keymapsDirPath) + keymap.loadDirectory(@keymapsDirPath) loadStylesheets: -> for stylesheetPath in @getStylesheetPaths() diff --git a/src/app/atom-theme.coffee b/src/app/atom-theme.coffee index f955402b5..b52ed3111 100644 --- a/src/app/atom-theme.coffee +++ b/src/app/atom-theme.coffee @@ -3,9 +3,16 @@ Theme = require 'theme' module.exports = class AtomTheme extends Theme + + loadStylesheet: (stylesheetPath)-> + @stylesheets[stylesheetPath] = fs.read(stylesheetPath) + load: -> - 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) + if /\.css$/.test(@path) + @loadStylesheet @path + else + json = fs.read(fs.join(@path, "package.json")) + for stylesheetName in JSON.parse(json).stylesheets + stylesheetPath = fs.join(@path, stylesheetName) + @loadStylesheet stylesheetPath super diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 455382926..e5eb0197f 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -50,15 +50,21 @@ _.extend atom, .filter (name) -> not _.contains(disabledPackages, name) loadThemes: -> - themeNames = config.get("core.themes") ? ['Atom - Dark', 'IR_Black'] + themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax'] themeNames = [themeNames] unless _.isArray(themeNames) @loadTheme(themeName) for themeName in themeNames + @loadUserStylesheet() loadTheme: (name) -> @loadedThemes.push Theme.load(name) + loadUserStylesheet: -> + userStylesheetPath = fs.join(config.configDirPath, 'user.css') + if fs.isFile(userStylesheetPath) + applyStylesheet(userStylesheetPath, fs.read(userStylesheetPath), 'userTheme') + getAtomThemeStylesheets: -> - themeNames = config.get("core.themes") ? ['Atom - Dark', 'IR_Black'] + themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax'] themeNames = [themeNames] unless _.isArray(themeNames) open: (args...) -> diff --git a/src/app/config.coffee b/src/app/config.coffee index bf35eff63..26efbd674 100644 --- a/src/app/config.coffee +++ b/src/app/config.coffee @@ -4,7 +4,7 @@ EventEmitter = require 'event-emitter' configDirPath = fs.absolute("~/.atom") configJsonPath = fs.join(configDirPath, "config.json") -userInitScriptPath = fs.join(configDirPath, "atom.coffee") +userInitScriptPath = fs.join(configDirPath, "user.coffee") bundledPackagesDirPath = fs.join(resourcePath, "src/packages") bundledThemesDirPath = fs.join(resourcePath, "themes") vendoredPackagesDirPath = fs.join(resourcePath, "vendor/packages") diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 1900995ad..c19088d87 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -18,7 +18,7 @@ class EditSession if fs.exists(state.buffer) session = project.buildEditSessionForPath(state.buffer) else - console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists" + console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists" if state.buffer session = project.buildEditSessionForPath(null) session.setScrollTop(state.scrollTop) session.setScrollLeft(state.scrollLeft) diff --git a/src/app/editor.coffee b/src/app/editor.coffee index fcc9a9409..1c0782304 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -14,7 +14,6 @@ _ = require 'underscore' module.exports = class Editor extends View @configDefaults: - fontFamily: "Inconsolata, Monaco, Courier" fontSize: 20 showInvisibles: false autosave: false @@ -352,11 +351,11 @@ class Editor extends View @hiddenInput.on 'focus', => @rootView()?.editorFocused(this) @isFocused = true - @addClass 'focused' + @addClass 'is-focused' @hiddenInput.on 'focusout', => @isFocused = false - @removeClass 'focused' + @removeClass 'is-focused' @autosave() if config.get "editor.autosave" @underlayer.on 'click', (e) => @@ -697,6 +696,7 @@ class Editor extends View parseInt(@css("font-size")) setFontFamily: (fontFamily) -> + return if fontFamily == undefined headTag = $("head") styleTag = headTag.find("style.font-family") if styleTag.length == 0 @@ -804,6 +804,10 @@ class Editor extends View @overlayer.append(view) calculateDimensions: -> + if not @isOnDom() + detachedEditorParent = _.last(@parents()) ? this + $(document.body).append(detachedEditorParent) + fragment = $('