diff --git a/docs/advanced/serialization.md b/docs/advanced/serialization.md index ce1d7a78e..282d9e358 100644 --- a/docs/advanced/serialization.md +++ b/docs/advanced/serialization.md @@ -71,27 +71,3 @@ will only attempt to call deserialize if the two versions match, and otherwise return undefined. We plan on implementing a migration system in the future, but this at least protects you from improperly deserializing old state. If you find yourself in dire need of the migration system, let us know. - -### Deferred Package Deserializers - -If your package defers loading on startup with an `activationEvents` property in -its `package.cson`, your deserializers won't be loaded until your package is -activated. If you want to deserialize an object from your package on startup, -this could be a problem. - -The solution is to also supply a `deferredDeserializers` array in your -`package.cson` with the names of all your deserializers. When Atom attempts to -deserialize some state whose `deserializer` matches one of these names, it will -load your package first so it can register any necessary deserializers before -proceeding. - -For example, the markdown preview package doesn't fully load until a preview is -triggered. But if you refresh a window with a preview pane, it loads the -markdown package early so Atom can deserialize the view correctly. - -```coffee-script -# markdown-preview/package.cson -'activationEvents': 'markdown-preview:toggle': '.editor' -'deferredDeserializers': ['MarkdownPreviewView'] -... -``` diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index c93079efa..750a1468f 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -68,6 +68,8 @@ 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' 'cmd--': 'window:decrease-font-size' + 'cmd-_': 'window:decrease-font-size' + 'cmd-0': 'window:reset-font-size' 'cmd-k up': 'pane:split-up' # Atom Specific 'cmd-k down': 'pane:split-down' # Atom Specific @@ -122,7 +124,6 @@ 'alt-cmd-z': 'editor:checkout-head-revision' 'cmd-<': 'editor:scroll-to-cursor' 'alt-cmd-ctrl-f': 'editor:fold-selection' - 'cmd-=': 'editor:auto-indent' # Sublime Parity 'cmd-enter': 'editor:newline-below' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 9c78e3622..ca306414e 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -40,6 +40,8 @@ 'ctrl-=': 'window:increase-font-size' 'ctrl-+': 'window:increase-font-size' 'ctrl--': 'window:decrease-font-size' + 'ctrl-_': 'window:decrease-font-size' + 'ctrl-0': 'window:reset-font-size' 'ctrl-k up': 'pane:split-up' # Atom Specific 'ctrl-k down': 'pane:split-down' # Atom Specific @@ -69,7 +71,6 @@ 'alt-ctrl-z': 'editor:checkout-head-revision' 'ctrl-<': 'editor:scroll-to-cursor' 'alt-ctrl-f': 'editor:fold-selection' - 'ctrl-=': 'editor:auto-indent' # Sublime Parity 'ctrl-enter': 'editor:newline-below' diff --git a/package.json b/package.json index cd96407dc..664f530d2 100644 --- a/package.json +++ b/package.json @@ -63,12 +63,12 @@ "base16-tomorrow-dark-theme": "0.11.0", "solarized-dark-syntax": "0.9.0", "solarized-light-syntax": "0.5.0", - "archive-view": "0.21.0", + "archive-view": "0.22.0", "autocomplete": "0.22.0", - "autoflow": "0.13.0", - "autosave": "0.10.0", - "background-tips": "0.6.0", - "bookmarks": "0.18.0", + "autoflow": "0.14.0", + "autosave": "0.11.0", + "background-tips": "0.7.0", + "bookmarks": "0.19.0", "bracket-matcher": "0.20.0", "command-logger": "0.11.0", "command-palette": "0.16.0", @@ -76,23 +76,23 @@ "editor-stats": "0.13.0", "exception-reporting": "0.13.0", "feedback": "0.23.0", - "find-and-replace": "0.81.0", + "find-and-replace": "0.82.0", "fuzzy-finder": "0.34.0", "gists": "0.17.0", "git-diff": "0.24.0", - "github-sign-in": "0.18.0", + "github-sign-in": "0.19.0", "go-to-line": "0.16.0", "grammar-selector": "0.19.0", "image-view": "0.19.0", - "keybinding-resolver": "0.9.0", + "keybinding-resolver": "0.10.0", "link": "0.17.0", - "markdown-preview": "0.25.1", + "markdown-preview": "0.28.0", "metrics": "0.26.0", "package-generator": "0.26.0", - "release-notes": "0.18.0", + "release-notes": "0.20.0", "settings-view": "0.71.0", "snippets": "0.27.0", - "spell-check": "0.23.0", + "spell-check": "0.24.0", "status-bar": "0.32.0", "styleguide": "0.23.0", "symbols-view": "0.33.0", @@ -101,10 +101,10 @@ "timecop": "0.13.0", "to-the-hubs": "0.19.0", "tree-view": "0.69.0", - "update-package-dependencies": "0.2.0", - "visual-bell": "0.6.0", + "update-package-dependencies": "0.3.0", + "visual-bell": "0.7.0", "welcome": "0.4.0", - "whitespace": "0.11.0", + "whitespace": "0.12.0", "wrap-guide": "0.14.0", "language-c": "0.4.0", "language-clojure": "0.1.0", @@ -136,7 +136,7 @@ "language-sql": "0.3.0", "language-text": "0.3.0", "language-todo": "0.3.0", - "language-toml": "0.7.0", + "language-toml": "0.8.0", "language-xml": "0.3.0", "language-yaml": "0.2.0" }, diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index b542e20e5..aca1da3ff 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -1,34 +1,49 @@ -{View, $, $$} = require '../src/space-pen-extensions' +path = require 'path' _ = require 'underscore-plus' {convertStackTrace} = require 'coffeestack' +{View, $, $$} = require '../src/space-pen-extensions' sourceMaps = {} -formatStackTrace = (stackTrace) -> +formatStackTrace = (message='', stackTrace) -> return stackTrace unless stackTrace jasminePattern = /^\s*at\s+.*\(?.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/ + firstJasmineLinePattern = /^\s*at \/.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/ convertedLines = [] for line in stackTrace.split('\n') convertedLines.push(line) unless jasminePattern.test(line) + break if firstJasmineLinePattern.test(line) - convertStackTrace(convertedLines.join('\n'), sourceMaps) + stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps) + lines = stackTrace.split('\n') + + # Remove first line of stack when it is the same as the error message + errorMatch = lines[0]?.match(/^Error: (.*)/) + lines.shift() if message.trim() is errorMatch?[1]?.trim() + + # Remove prefix of lines matching: at [object Object]. (path:1:2) + for line, index in lines + prefixMatch = line.match(/at \[object Object\]\. \(([^\)]+)\)/) + lines[index] = "at #{prefixMatch[1]}" if prefixMatch + + lines = lines.map (line) -> line.trim() + lines.join('\n') module.exports = class AtomReporter extends View @content: -> - @div id: 'HTMLReporter', class: 'jasmine_reporter', => - @div outlet: 'specPopup', class: "spec-popup" + @div class: 'spec-reporter', => @div outlet: "suites" - @div outlet: 'coreArea', => - @div outlet: 'coreHeader', class: 'symbolHeader' - @ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled' - @div outlet: 'bundledArea', => - @div outlet: 'bundledHeader', class: 'symbolHeader' - @ul outlet: 'bundledSummary', class: 'symbolSummary list-unstyled' - @div outlet: 'userArea', => - @div outlet: 'userHeader', class: 'symbolHeader' - @ul outlet: 'userSummary', class: 'symbolSummary list-unstyled' - @div outlet: "status", class: 'status', => + @div outlet: 'coreArea', class: 'symbol-area', => + @div outlet: 'coreHeader', class: 'symbol-header' + @ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled' + @div outlet: 'bundledArea', class: 'symbol-area', => + @div outlet: 'bundledHeader', class: 'symbol-header' + @ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled' + @div outlet: 'userArea', class: 'symbol-area', => + @div outlet: 'userHeader', class: 'symbol-header' + @ul outlet: 'userSummary', class: 'symbol-summary list-unstyled' + @div outlet: "status", class: 'status alert alert-info', => @div outlet: "time", class: 'time' @div outlet: "specCount", class: 'spec-count' @div outlet: "message", class: 'message' @@ -45,7 +60,7 @@ class AtomReporter extends View reportRunnerStarting: (runner) -> @handleEvents() - @startedAt = new Date() + @startedAt = Date.now() specs = runner.specs() @totalSpecCount = specs.length @addSpecs(specs) @@ -53,57 +68,29 @@ class AtomReporter extends View reportRunnerResults: (runner) -> @updateSpecCounts() - if @failedCount == 0 - @message.text "Success!" + @status.addClass('alert-success').removeClass('alert-info') if @failedCount is 0 + if @failedCount is 1 + @message.text "#{@failedCount} failure" else - @message.text "Game Over" + @message.text "#{@failedCount} failures" reportSuiteResults: (suite) -> reportSpecResults: (spec) -> @completeSpecCount++ - spec.endedAt = new Date().getTime() + spec.endedAt = Date.now() @specComplete(spec) @updateStatusView(spec) reportSpecStarting: (spec) -> @specStarted(spec) - specFilter: (spec) -> - globalFocusPriority = jasmine.getEnv().focusPriority - parent = spec.parentSuite ? spec.suite - - if !globalFocusPriority - true - else if spec.focusPriority >= globalFocusPriority - true - else if not parent - false - else - @specFilter(parent) - handleEvents: -> - $(document).on "mouseover", ".spec-summary", ({currentTarget}) => - element = $(currentTarget) - description = element.data("description") - return unless description - - clearTimeout @timeoutId if @timeoutId? - @specPopup.show() - spec = _.find(window.timedSpecs, ({fullName}) -> description is fullName) - description = "#{description} #{spec.time}ms" if spec - @specPopup.text description - {left, top} = element.offset() - left += 20 - top += 20 - @specPopup.offset({left, top}) - @timeoutId = setTimeout((=> @specPopup.hide()), 3000) - $(document).on "click", ".spec-toggle", ({currentTarget}) => element = $(currentTarget) specFailures = element.parent().find('.spec-failures') specFailures.toggle() - if specFailures.is(":visible") then element.text "\uf03d" else element.html "\uf03f" + element.toggleClass('folded') false updateSpecCounts: -> @@ -115,7 +102,7 @@ class AtomReporter extends View updateStatusView: (spec) -> if @failedCount > 0 - @status.addClass('failed') unless @status.hasClass('failed') + @status.addClass('alert-danger').removeClass('alert-info') @updateSpecCounts() @@ -123,7 +110,7 @@ class AtomReporter extends View rootSuite = rootSuite.parentSuite while rootSuite.parentSuite @message.text rootSuite.description - time = "#{Math.round((spec.endedAt - @startedAt.getTime()) / 10)}" + time = "#{Math.round((spec.endedAt - @startedAt) / 10)}" time = "0#{time}" if time.length < 3 @time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s" @@ -145,15 +132,22 @@ class AtomReporter extends View @userSummary.append symbol if coreSpecs > 0 - @coreHeader.text("Core Specs (#{coreSpecs}):") + @coreHeader.text("Core Specs (#{coreSpecs})") else @coreArea.hide() if bundledPackageSpecs > 0 - @bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs}):") + @bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})") else @bundledArea.hide() if userPackageSpecs > 0 - @userHeader.text("User Package Specs (#{userPackageSpecs}):") + if coreSpecs is 0 and bundledPackageSpecs is 0 + # Package specs being run, show a more descriptive label + {specDirectory} = specs[0] + packageFolderName = path.basename(path.dirname(specDirectory)) + packageName = _.undasherize(_.uncamelcase(packageFolderName)) + @userHeader.text("#{packageName} Specs") + else + @userHeader.text("User Package Specs (#{userPackageSpecs})") else @userArea.hide() @@ -163,7 +157,7 @@ class AtomReporter extends View specComplete: (spec) -> specSummaryElement = $("#spec-summary-#{spec.id}") specSummaryElement.removeClass('pending') - specSummaryElement.data("description", spec.getFullName()) + specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter') results = spec.results() if results.skipped @@ -184,11 +178,9 @@ class SuiteResultView extends View @div class: 'suite', => @div outlet: 'description', class: 'description' - suite: null - initialize: (@suite) -> @attr('id', "suite-view-#{@suite.id}") - @description.html @suite.description + @description.text(@suite.description) attach: -> (@parentSuiteView() or $('.results')).append this @@ -205,20 +197,22 @@ class SuiteResultView extends View class SpecResultView extends View @content: -> @div class: 'spec', => - @div "\uf03d", class: 'spec-toggle' + @div class: 'spec-toggle' @div outlet: 'description', class: 'description' @div outlet: 'specFailures', class: 'spec-failures' - spec: null initialize: (@spec) -> @addClass("spec-view-#{@spec.id}") - @description.text @spec.description + + description = @spec.description + description = "it #{description}" if description.indexOf('it ') isnt 0 + @description.text(description) for result in @spec.results().getItems() when not result.passed() - stackTrace = formatStackTrace(result.trace.stack) + stackTrace = formatStackTrace(result.message, result.trace.stack) @specFailures.append $$ -> - @div result.message, class: 'resultMessage fail' - @div stackTrace, class: 'stackTrace' if stackTrace + @div result.message, class: 'result-message fail' + @pre stackTrace, class: 'stack-trace padded' if stackTrace attach: -> @parentSuiteView().append this diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index 9e37e2ac6..038318b07 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -10,25 +10,14 @@ describe "the `atom` global", -> describe "package lifecycle methods", -> describe ".loadPackage(name)", -> - describe "when the package has deferred deserializers", -> - it "requires the package's main module if one of its deferred deserializers is referenced", -> - pack = atom.packages.loadPackage('package-with-activation-events') - spyOn(pack, 'activateStylesheets').andCallThrough() - expect(pack.mainModule).toBeNull() - object = atom.deserializers.deserialize({deserializer: 'Foo', data: 5}) - expect(pack.mainModule).toBeDefined() - expect(object.constructor.name).toBe 'Foo' - expect(object.data).toBe 5 - expect(pack.activateStylesheets).toHaveBeenCalled() + it "continues if the package has an invalid package.json", -> + spyOn(console, 'warn') + atom.config.set("core.disabledPackages", []) + expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow() - it "continues if the package has an invalid package.json", -> - spyOn(console, 'warn') - atom.config.set("core.disabledPackages", []) - expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow() - - it "continues if the package has an invalid keymap", -> - atom.config.set("core.disabledPackages", []) - expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow() + it "continues if the package has an invalid keymap", -> + atom.config.set("core.disabledPackages", []) + expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow() describe ".unloadPackage(name)", -> describe "when the package is active", -> @@ -355,12 +344,18 @@ describe "the `atom` global", -> it "absorbs exceptions that are thrown by the package module's serialize methods", -> spyOn(console, 'error') - atom.packages.activatePackage('package-with-serialize-error', immediate: true) - atom.packages.activatePackage('package-with-serialization', immediate: true) - atom.packages.deactivatePackages() - expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() - expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 - expect(console.error).toHaveBeenCalled() + + waitsForPromise -> + atom.packages.activatePackage('package-with-serialize-error') + + waitsForPromise -> + atom.packages.activatePackage('package-with-serialization') + + runs -> + atom.packages.deactivatePackages() + expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined() + expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1 + expect(console.error).toHaveBeenCalled() it "removes the package's grammars", -> waitsForPromise -> @@ -405,15 +400,22 @@ describe "the `atom` global", -> describe "textmate packages", -> it "removes the package's grammars", -> expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar" - atom.packages.activatePackage('language-ruby', sync: true) - expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby" - atom.packages.deactivatePackage('language-ruby') - expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar" + + waitsForPromise -> + atom.packages.activatePackage('language-ruby') + + runs -> + expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby" + atom.packages.deactivatePackage('language-ruby') + expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar" it "removes the package's scoped properties", -> - atom.packages.activatePackage('language-ruby', sync: true) - atom.packages.deactivatePackage('language-ruby') - expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined() + waitsForPromise -> + atom.packages.activatePackage('language-ruby') + + runs -> + atom.packages.deactivatePackage('language-ruby') + expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined() describe ".activate()", -> packageActivator = null diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 9f8afa408..02ef6d6d8 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -63,6 +63,19 @@ describe "Config", -> atom.config.toggle('foo.a') expect(atom.config.get('foo.a')).toBe false + describe ".restoreDefault(keyPath)", -> + it "sets the value of the key path to its default", -> + atom.config.setDefaults('a', b: 3) + atom.config.set('a.b', 4) + expect(atom.config.get('a.b')).toBe 4 + atom.config.restoreDefault('a.b') + expect(atom.config.get('a.b')).toBe 3 + + atom.config.set('a.c', 5) + expect(atom.config.get('a.c')).toBe 5 + atom.config.restoreDefault('a.c') + expect(atom.config.get('a.c')).toBeUndefined() + describe ".pushAtKeyPath(keyPath, value)", -> it "pushes the given value to the array at the key path and updates observers", -> atom.config.set("foo.bar.baz", ["a"]) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index e85a05049..6e13ee8f4 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -5,12 +5,15 @@ describe "DisplayBuffer", -> [displayBuffer, buffer, changeHandler, tabLength] = [] beforeEach -> tabLength = 2 - atom.packages.activatePackage('language-javascript', sync: true) + buffer = atom.project.bufferForPathSync('sample.js') displayBuffer = new DisplayBuffer({buffer, tabLength}) changeHandler = jasmine.createSpy 'changeHandler' displayBuffer.on 'changed', changeHandler + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + afterEach -> displayBuffer.destroy() buffer.release() diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 8d5e05d9a..821299f8c 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -16,11 +16,13 @@ describe "Editor", -> describe "with default options", -> beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) editor = atom.project.openSync('sample.js', autoIndent: false) buffer = editor.buffer lineLengths = buffer.getLines().map (line) -> line.length + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + describe "when the editor is deserialized", -> it "restores selections and folds based on markers in the buffer", -> editor.setSelectedBufferRange([[1, 2], [3, 4]]) @@ -2733,19 +2735,26 @@ describe "Editor", -> describe "when the editor's grammar has an injection selector", -> beforeEach -> - atom.packages.activatePackage('language-text', sync: true) - atom.packages.activatePackage('language-javascript', sync: true) + + waitsForPromise -> + atom.packages.activatePackage('language-text') + + waitsForPromise -> + atom.packages.activatePackage('language-javascript') it "includes the grammar's patterns when the selector matches the current scope in other grammars", -> - atom.packages.activatePackage('language-hyperlink', sync: true) - grammar = atom.syntax.selectGrammar("text.js") - {tokens} = grammar.tokenizeLine("var i; // http://github.com") + waitsForPromise -> + atom.packages.activatePackage('language-hyperlink') - expect(tokens[0].value).toBe "var" - expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"] + runs -> + grammar = atom.syntax.selectGrammar("text.js") + {tokens} = grammar.tokenizeLine("var i; // http://github.com") - expect(tokens[6].value).toBe "http://github.com" - expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] + expect(tokens[0].value).toBe "var" + expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"] + + expect(tokens[6].value).toBe "http://github.com" + expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] describe "when the grammar is added", -> it "retokenizes existing buffers that contain tokens that match the injection selector", -> @@ -2756,11 +2765,13 @@ describe "Editor", -> expect(tokens[1].value).toBe " http://github.com" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] - atom.packages.activatePackage('language-hyperlink', sync: true) + waitsForPromise -> + atom.packages.activatePackage('language-hyperlink') - {tokens} = editor.lineForScreenRow(0) - expect(tokens[2].value).toBe "http://github.com" - expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] + runs -> + {tokens} = editor.lineForScreenRow(0) + expect(tokens[2].value).toBe "http://github.com" + expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"] describe "when the grammar is updated", -> it "retokenizes existing buffers that contain tokens that match the injection selector", -> @@ -2771,14 +2782,17 @@ describe "Editor", -> expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] - atom.packages.activatePackage('package-with-injection-selector', sync: true) + + atom.packages.activatePackage('package-with-injection-selector') {tokens} = editor.lineForScreenRow(0) expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] - atom.packages.activatePackage('language-sql', sync: true) + waitsForPromise -> + atom.packages.activatePackage('language-sql') - {tokens} = editor.lineForScreenRow(0) - expect(tokens[2].value).toBe "SELECT" - expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"] + runs -> + {tokens} = editor.lineForScreenRow(0) + expect(tokens[2].value).toBe "SELECT" + expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"] diff --git a/spec/editor-view-spec.coffee b/spec/editor-view-spec.coffee index b63079d11..33db23b42 100644 --- a/spec/editor-view-spec.coffee +++ b/spec/editor-view-spec.coffee @@ -10,8 +10,6 @@ describe "EditorView", -> [buffer, editorView, editor, cachedLineHeight, cachedCharWidth] = [] beforeEach -> - atom.packages.activatePackage('language-text', sync: true) - atom.packages.activatePackage('language-javascript', sync: true) editor = atom.project.openSync('sample.js') buffer = editor.buffer editorView = new EditorView(editor) @@ -26,6 +24,12 @@ describe "EditorView", -> @width(getCharWidth() * widthInChars) if widthInChars $('#jasmine-content').append(this) + waitsForPromise -> + atom.packages.activatePackage('language-text', sync: true) + + waitsForPromise -> + atom.packages.activatePackage('language-javascript', sync: true) + getLineHeight = -> return cachedLineHeight if cachedLineHeight? calcDimensions() @@ -2532,6 +2536,52 @@ describe "EditorView", -> editorView.trigger 'editor:move-line-up' expect(editor.getCursorBufferPosition()).toEqual [0,2] + describe "when the line above is folded", -> + it "moves the line around the fold", -> + editor.foldBufferRow(1) + editor.setCursorBufferPosition([10, 0]) + editorView.trigger 'editor:move-line-up' + + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(buffer.lineForRow(1)).toBe '' + expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {' + expect(editor.isFoldedAtBufferRow(1)).toBe false + expect(editor.isFoldedAtBufferRow(2)).toBe true + + describe "when the line being moved is folded", -> + it "moves the fold around the fold above it", -> + editor.setCursorBufferPosition([0, 0]) + editor.insertText """ + var a = function() { + b = 3; + }; + + """ + editor.foldBufferRow(0) + editor.foldBufferRow(3) + editor.setCursorBufferPosition([3, 0]) + editorView.trigger 'editor:move-line-up' + + expect(editor.getCursorBufferPosition()).toEqual [0, 0] + expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' + expect(buffer.lineForRow(13)).toBe 'var a = function() {' + editor.logScreenLines() + expect(editor.isFoldedAtBufferRow(0)).toBe true + expect(editor.isFoldedAtBufferRow(13)).toBe true + + describe "when the line above is empty and the line above that is folded", -> + it "moves the line to the empty line", -> + editor.foldBufferRow(2) + editor.setCursorBufferPosition([11, 0]) + editorView.trigger 'editor:move-line-up' + + expect(editor.getCursorBufferPosition()).toEqual [10, 0] + expect(buffer.lineForRow(9)).toBe ' };' + expect(buffer.lineForRow(10)).toBe ' return sort(Array.apply(this, arguments));' + expect(buffer.lineForRow(11)).toBe '' + expect(editor.isFoldedAtBufferRow(2)).toBe true + expect(editor.isFoldedAtBufferRow(10)).toBe false + describe "where there is a selection", -> describe "when the selection falls inside the line", -> it "maintains the selection", -> @@ -2631,6 +2681,54 @@ describe "EditorView", -> editorView.trigger 'editor:move-line-down' expect(editor.getCursorBufferPosition()).toEqual [1, 2] + describe "when the line below is folded", -> + it "moves the line around the fold", -> + editor.setCursorBufferPosition([0, 0]) + editor.foldBufferRow(1) + editorView.trigger 'editor:move-line-down' + + expect(editor.getCursorBufferPosition()).toEqual [9, 0] + expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' + expect(buffer.lineForRow(9)).toBe 'var quicksort = function () {' + expect(editor.isFoldedAtBufferRow(0)).toBe true + expect(editor.isFoldedAtBufferRow(9)).toBe false + + describe "when the line being moved is folded", -> + it "moves the fold around the fold below it", -> + editor.setCursorBufferPosition([0, 0]) + editor.insertText """ + var a = function() { + b = 3; + }; + + """ + editor.foldBufferRow(0) + editor.foldBufferRow(3) + editor.setCursorBufferPosition([0, 0]) + editorView.trigger 'editor:move-line-down' + + expect(editor.getCursorBufferPosition()).toEqual [13, 0] + expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' + expect(buffer.lineForRow(13)).toBe 'var a = function() {' + expect(editor.isFoldedAtBufferRow(0)).toBe true + expect(editor.isFoldedAtBufferRow(13)).toBe true + + describe "when the line below is empty and the line below that is folded", -> + it "moves the line to the empty line", -> + editor.setCursorBufferPosition([0, Infinity]) + editor.insertText('\n') + editor.setCursorBufferPosition([0, 0]) + editor.foldBufferRow(2) + editorView.trigger 'editor:move-line-down' + + expect(editor.getCursorBufferPosition()).toEqual [1, 0] + expect(buffer.lineForRow(0)).toBe '' + expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' + expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {' + expect(editor.isFoldedAtBufferRow(0)).toBe false + expect(editor.isFoldedAtBufferRow(1)).toBe false + expect(editor.isFoldedAtBufferRow(2)).toBe true + describe "when the cursor is on the last line", -> it "does not move the line", -> editor.moveCursorToBottom() diff --git a/spec/fixtures/packages/package-with-activation-events/package.cson b/spec/fixtures/packages/package-with-activation-events/package.cson index 45d8dfea1..dfa55c54d 100644 --- a/spec/fixtures/packages/package-with-activation-events/package.cson +++ b/spec/fixtures/packages/package-with-activation-events/package.cson @@ -1,2 +1 @@ 'activationEvents': ['activation-event'] -'deferredDeserializers': ['Foo'] diff --git a/spec/fixtures/packages/package-with-serialize-error/package.cson b/spec/fixtures/packages/package-with-serialize-error/package.cson index d49a175ed..300ce2fc0 100644 --- a/spec/fixtures/packages/package-with-serialize-error/package.cson +++ b/spec/fixtures/packages/package-with-serialize-error/package.cson @@ -1,5 +1 @@ -# This package loads async, otherwise it would log errors when it -# is automatically serialized when workspaceView is deactivatated - 'main': 'index.coffee' -'activationEvents': ['activation-event'] diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 2f7b1cc91..a72470704 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -6,10 +6,12 @@ describe "LanguageMode", -> describe "javascript", -> beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) editor = atom.project.openSync('sample.js', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + describe ".minIndentLevelForRowRange(startRow, endRow)", -> it "returns the minimum indent level for the given row range", -> expect(languageMode.minIndentLevelForRowRange(4, 7)).toBe 2 @@ -100,10 +102,12 @@ describe "LanguageMode", -> describe "coffeescript", -> beforeEach -> - atom.packages.activatePackage('language-coffee-script', sync: true) editor = atom.project.openSync('coffee.coffee', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + describe ".toggleLineCommentsForBufferRows(start, end)", -> it "comments/uncomments lines in the given range", -> languageMode.toggleLineCommentsForBufferRows(4, 6) @@ -147,10 +151,12 @@ describe "LanguageMode", -> describe "css", -> beforeEach -> - atom.packages.activatePackage('language-css', sync: true) editor = atom.project.openSync('css.css', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-css') + describe ".toggleLineCommentsForBufferRows(start, end)", -> it "comments/uncomments lines in the given range", -> languageMode.toggleLineCommentsForBufferRows(0, 1) @@ -188,11 +194,15 @@ describe "LanguageMode", -> describe "less", -> beforeEach -> - atom.packages.activatePackage('language-less', sync: true) - atom.packages.activatePackage('language-css', sync: true) editor = atom.project.openSync('sample.less', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-less') + + waitsForPromise -> + atom.packages.activatePackage('language-css') + describe "when commenting lines", -> it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", -> languageMode.toggleLineCommentsForBufferRows(0, 0) @@ -200,10 +210,12 @@ describe "LanguageMode", -> describe "folding", -> beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) editor = atom.project.openSync('sample.js', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + it "maintains cursor buffer position when a folding/unfolding", -> editor.setCursorBufferPosition([5,5]) languageMode.foldAll() @@ -298,10 +310,12 @@ describe "LanguageMode", -> describe "folding with comments", -> beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) editor = atom.project.openSync('sample-with-comments.js', autoIndent: false) {buffer, languageMode} = editor + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + describe ".unfoldAll()", -> it "unfolds every folded line", -> initialScreenLineCount = editor.getScreenLineCount() @@ -362,10 +376,12 @@ describe "LanguageMode", -> describe "css", -> beforeEach -> - atom.packages.activatePackage('language-source', sync: true) - atom.packages.activatePackage('language-css', sync: true) editor = atom.project.openSync('css.css', autoIndent: true) + waitsForPromise -> + atom.packages.activatePackage('language-source') + atom.packages.activatePackage('language-css') + describe "suggestedIndentForBufferRow", -> it "does not return negative values (regression)", -> editor.setText('.test {\npadding: 0;\n}') diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index c93dcf4fd..8e9fa939d 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -159,6 +159,16 @@ addCustomMatchers = (spec) -> @message = -> return "Expected path '" + @actual + "'" + notText + " to exist." fs.existsSync(@actual) + toHaveFocus: -> + notText = this.isNot and " not" or "" + if not document.hasFocus() + console.error "Specs will fail because the Dev Tools have focus. To fix this close the Dev Tools or click the spec runner." + + @message = -> return "Expected element '" + @actual + "' or its descendants" + notText + " to have focus." + element = @actual + element = element.get(0) if element.jquery + element.webkitMatchesSelector(":focus") or element.querySelector(":focus") + window.keyIdentifierForKey = (key) -> if key.length > 1 # named key key diff --git a/spec/syntax-spec.coffee b/spec/syntax-spec.coffee index 92373c53d..c39d12152 100644 --- a/spec/syntax-spec.coffee +++ b/spec/syntax-spec.coffee @@ -4,10 +4,18 @@ temp = require 'temp' describe "the `syntax` global", -> beforeEach -> - atom.packages.activatePackage('language-text', sync: true) - atom.packages.activatePackage('language-javascript', sync: true) - atom.packages.activatePackage('language-coffee-script', sync: true) - atom.packages.activatePackage('language-ruby', sync: true) + + waitsForPromise -> + atom.packages.activatePackage('language-text') + + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + + waitsForPromise -> + atom.packages.activatePackage('language-ruby') describe "serialization", -> it "remembers grammar overrides by path", -> @@ -20,29 +28,33 @@ describe "the `syntax` global", -> describe ".selectGrammar(filePath)", -> it "can use the filePath to load the correct grammar based on the grammar's filetype", -> - atom.packages.activatePackage('language-git', sync: true) + waitsForPromise -> + atom.packages.activatePackage('language-git') - expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js) - expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config) - expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile) - expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar" - expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar" + runs -> + expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js) + expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config) + expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile) + expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar" + expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar" it "uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", -> filePath = require.resolve("./fixtures/shebang") expect(atom.syntax.selectGrammar(filePath).name).toBe "Ruby" it "uses the number of newlines in the first line regex to determine the number of lines to test against", -> - atom.packages.activatePackage('language-property-list', sync: true) + waitsForPromise -> + atom.packages.activatePackage('language-property-list') - fileContent = "first-line\n" - expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript" + runs -> + fileContent = "first-line\n" + expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript" - fileContent = '' - expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar" + fileContent = '' + expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar" - fileContent += '\n' - expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)" + fileContent += '\n' + expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)" it "doesn't read the file when the file contents are specified", -> filePath = require.resolve("./fixtures/shebang") diff --git a/spec/text-buffer-spec.coffee b/spec/text-buffer-spec.coffee index 7f26c663b..70e1a773f 100644 --- a/spec/text-buffer-spec.coffee +++ b/spec/text-buffer-spec.coffee @@ -571,6 +571,23 @@ describe 'TextBuffer', -> saveBuffer.reload() expect(events).toEqual ['will-reload', 'reloaded'] + it "no longer reports being in conflict", -> + saveBuffer.setText('a') + saveBuffer.save() + saveBuffer.setText('ab') + + fs.writeFileSync(saveBuffer.getPath(), 'c') + conflictHandler = jasmine.createSpy('conflictHandler') + saveBuffer.on 'contents-conflicted', conflictHandler + + waitsFor -> + conflictHandler.callCount > 0 + + runs -> + expect(saveBuffer.isInConflict()).toBe true + saveBuffer.save() + expect(saveBuffer.isInConflict()).toBe false + describe "when the buffer has no path", -> it "throws an exception", -> saveBuffer = atom.project.bufferForPathSync(null) diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index a9df78a56..2fad95256 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -5,11 +5,13 @@ describe "TokenizedBuffer", -> [tokenizedBuffer, buffer, changeHandler] = [] beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) # enable async tokenization TokenizedBuffer.prototype.chunkSize = 5 jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground') + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + startTokenizing = (tokenizedBuffer) -> tokenizedBuffer.setVisible(true) @@ -311,10 +313,13 @@ describe "TokenizedBuffer", -> describe "when the buffer contains hard-tabs", -> beforeEach -> - atom.packages.activatePackage('language-coffee-script', sync: true) - buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee') - tokenizedBuffer = new TokenizedBuffer({buffer}) - startTokenizing(tokenizedBuffer) + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + + runs -> + buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee') + tokenizedBuffer = new TokenizedBuffer({buffer}) + startTokenizing(tokenizedBuffer) afterEach -> tokenizedBuffer.destroy() @@ -341,14 +346,17 @@ describe "TokenizedBuffer", -> describe "when the buffer contains surrogate pairs", -> beforeEach -> - atom.packages.activatePackage('language-javascript', sync: true) - buffer = atom.project.bufferForPathSync 'sample-with-pairs.js' - buffer.setText """ - 'abc\uD835\uDF97def' - //\uD835\uDF97xyz - """ - tokenizedBuffer = new TokenizedBuffer({buffer}) - fullyTokenize(tokenizedBuffer) + waitsForPromise -> + atom.packages.activatePackage('language-javascript') + + runs -> + buffer = atom.project.bufferForPathSync 'sample-with-pairs.js' + buffer.setText """ + 'abc\uD835\uDF97def' + //\uD835\uDF97xyz + """ + tokenizedBuffer = new TokenizedBuffer({buffer}) + fullyTokenize(tokenizedBuffer) afterEach -> tokenizedBuffer.destroy() @@ -379,22 +387,30 @@ describe "TokenizedBuffer", -> describe "when the grammar is updated because a grammar it includes is activated", -> it "retokenizes the buffer", -> - atom.packages.activatePackage('language-ruby-on-rails', sync: true) - atom.packages.activatePackage('language-ruby', sync: true) - buffer = atom.project.bufferForPathSync() - buffer.setText "
<%= User.find(2).full_name %>
" - tokenizedBuffer = new TokenizedBuffer({buffer}) - tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb')) - fullyTokenize(tokenizedBuffer) + waitsForPromise -> + atom.packages.activatePackage('language-ruby-on-rails') - {tokens} = tokenizedBuffer.lineForScreenRow(0) - expect(tokens[0]).toEqual value: "
", scopes: ["text.html.ruby"] + waitsForPromise -> + atom.packages.activatePackage('language-ruby') - atom.packages.activatePackage('language-html', sync: true) - fullyTokenize(tokenizedBuffer) - {tokens} = tokenizedBuffer.lineForScreenRow(0) - expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"] + runs -> + buffer = atom.project.bufferForPathSync() + buffer.setText "
<%= User.find(2).full_name %>
" + tokenizedBuffer = new TokenizedBuffer({buffer}) + tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb')) + fullyTokenize(tokenizedBuffer) + + {tokens} = tokenizedBuffer.lineForScreenRow(0) + expect(tokens[0]).toEqual value: "
", scopes: ["text.html.ruby"] + + waitsForPromise -> + atom.packages.activatePackage('language-html') + + runs -> + fullyTokenize(tokenizedBuffer) + {tokens} = tokenizedBuffer.lineForScreenRow(0) + expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"] describe ".tokenForPosition(position)", -> afterEach -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 937069482..fd99c3d74 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -7,49 +7,125 @@ describe "Workspace", -> atom.project.setPath(atom.project.resolve('dir')) workspace = new Workspace - describe "::open(uri)", -> + describe "::open(uri, options)", -> beforeEach -> - spyOn(workspace.activePane, 'activate') - - describe "when called without a uri", -> - it "adds and activates an empty editor on the active pane", -> - editor = null - waitsForPromise -> - workspace.open().then (o) -> editor = o - - runs -> - expect(editor.getPath()).toBeUndefined() - expect(workspace.activePane.items).toEqual [editor] - expect(workspace.activePaneItem).toBe editor - expect(workspace.activePane.activate).toHaveBeenCalled() - - describe "when called with a uri", -> - describe "when the active pane already has an editor for the given uri", -> - it "activates the existing editor on the active pane", -> - editor1 = workspace.openSync('a') - editor2 = workspace.openSync('b') + spyOn(workspace.activePane, 'activate').andCallThrough() + describe "when the 'searchAllPanes' option is false (default)", -> + describe "when called without a uri", -> + it "adds and activates an empty editor on the active pane", -> editor = null waitsForPromise -> - workspace.open('a').then (o) -> editor = o + workspace.open().then (o) -> editor = o runs -> - expect(editor).toBe editor1 - expect(workspace.activePaneItem).toBe editor - expect(workspace.activePane.activate).toHaveBeenCalled() - - describe "when the active pane does not have an editor for the given uri", -> - it "adds and activates a new editor for the given path on the active pane", -> - editor = null - waitsForPromise -> - workspace.open('a').then (o) -> editor = o - - runs -> - expect(editor.getUri()).toBe 'a' - expect(workspace.activePaneItem).toBe editor + expect(editor.getPath()).toBeUndefined() expect(workspace.activePane.items).toEqual [editor] + expect(workspace.activePaneItem).toBe editor expect(workspace.activePane.activate).toHaveBeenCalled() + describe "when called with a uri", -> + describe "when the active pane already has an editor for the given uri", -> + it "activates the existing editor on the active pane", -> + editor1 = workspace.openSync('a') + editor2 = workspace.openSync('b') + + editor = null + waitsForPromise -> + workspace.open('a').then (o) -> editor = o + + runs -> + expect(editor).toBe editor1 + expect(workspace.activePaneItem).toBe editor + expect(workspace.activePane.activate).toHaveBeenCalled() + + describe "when the active pane does not have an editor for the given uri", -> + it "adds and activates a new editor for the given path on the active pane", -> + editor = null + waitsForPromise -> + workspace.open('a').then (o) -> editor = o + + runs -> + expect(editor.getUri()).toBe 'a' + expect(workspace.activePaneItem).toBe editor + expect(workspace.activePane.items).toEqual [editor] + expect(workspace.activePane.activate).toHaveBeenCalled() + + describe "when the 'searchAllPanes' option is true", -> + describe "when an editor for the given uri is already open on an inactive pane", -> + it "activates the existing editor on the inactive pane, then activates that pane", -> + editor1 = workspace.openSync('a') + pane1 = workspace.activePane + pane2 = workspace.activePane.splitRight() + editor2 = workspace.openSync('b') + expect(workspace.activePaneItem).toBe editor2 + + waitsForPromise -> + workspace.open('a', searchAllPanes: true) + + runs -> + expect(workspace.activePane).toBe pane1 + expect(workspace.activePaneItem).toBe editor1 + + describe "when no editor for the given uri is open in any pane", -> + it "opens an editor for the given uri in the active pane", -> + editor = null + waitsForPromise -> + workspace.open('a', searchAllPanes: true).then (o) -> editor = o + + runs -> + expect(workspace.activePaneItem).toBe editor + + describe "when the 'split' option is set", -> + describe "when the 'split' option is 'left'", -> + it "opens the editor in the leftmost pane of the current pane axis", -> + pane1 = workspace.activePane + pane2 = pane1.splitRight() + expect(workspace.activePane).toBe pane2 + + editor = null + waitsForPromise -> + workspace.open('a', split: 'left').then (o) -> editor = o + + runs -> + expect(workspace.activePane).toBe pane1 + expect(pane1.items).toEqual [editor] + expect(pane2.items).toEqual [] + + # Focus right pane and reopen the file on the left + waitsForPromise -> + pane2.focus() + workspace.open('a', split: 'left').then (o) -> editor = o + + runs -> + expect(workspace.activePane).toBe pane1 + expect(pane1.items).toEqual [editor] + expect(pane2.items).toEqual [] + + describe "when the 'split' option is 'right'", -> + it "opens the editor in the rightmost pane of the current pane axis", -> + editor = null + pane1 = workspace.activePane + pane2 = null + waitsForPromise -> + workspace.open('a', split: 'right').then (o) -> editor = o + + runs -> + pane2 = workspace.getPanes().filter((p) -> p != pane1)[0] + expect(workspace.activePane).toBe pane2 + expect(pane1.items).toEqual [] + expect(pane2.items).toEqual [editor] + + # Focus right pane and reopen the file on the right + waitsForPromise -> + pane1.focus() + workspace.open('a', split: 'right').then (o) -> editor = o + + runs -> + expect(workspace.activePane).toBe pane2 + expect(pane1.items).toEqual [] + expect(pane2.items).toEqual [editor] + describe "::openSync(uri, options)", -> [activePane, initialItemCount] = [] @@ -92,61 +168,6 @@ describe "Workspace", -> workspace.openSync('b', activatePane: false) expect(activePane.activate).not.toHaveBeenCalled() - describe "::openSingletonSync(uri, options)", -> - describe "when an editor for the given uri is already open on the active pane", -> - it "activates the existing editor", -> - editor1 = workspace.openSync('a') - editor2 = workspace.openSync('b') - expect(workspace.activePaneItem).toBe editor2 - workspace.openSingletonSync('a') - expect(workspace.activePaneItem).toBe editor1 - - describe "when an editor for the given uri is already open on an inactive pane", -> - it "activates the existing editor on the inactive pane, then activates that pane", -> - editor1 = workspace.openSync('a') - pane1 = workspace.activePane - pane2 = workspace.activePane.splitRight() - editor2 = workspace.openSync('b') - expect(workspace.activePaneItem).toBe editor2 - workspace.openSingletonSync('a') - expect(workspace.activePane).toBe pane1 - expect(workspace.activePaneItem).toBe editor1 - - describe "when no editor for the given uri is open in any pane", -> - it "opens an editor for the given uri in the active pane", -> - editor1 = workspace.openSingletonSync('a') - expect(workspace.activePaneItem).toBe editor1 - - describe "when the 'split' option is 'left'", -> - it "opens the editor in the leftmost pane of the current pane axis", -> - pane1 = workspace.activePane - pane2 = pane1.splitRight() - expect(workspace.activePane).toBe pane2 - editor1 = workspace.openSingletonSync('a', split: 'left') - expect(workspace.activePane).toBe pane1 - expect(pane1.items).toEqual [editor1] - expect(pane2.items).toEqual [] - - describe "when the 'split' option is 'right'", -> - describe "when the active pane is in a horizontal pane axis", -> - it "activates the editor on the rightmost pane of the current pane axis", -> - pane1 = workspace.activePane - pane2 = pane1.splitRight() - pane1.activate() - editor1 = workspace.openSingletonSync('a', split: 'right') - expect(workspace.activePane).toBe pane2 - expect(pane2.items).toEqual [editor1] - expect(pane1.items).toEqual [] - - describe "when the active pane is not in a horizontal pane axis", -> - it "splits the current pane to the right, then activates the editor on the right pane", -> - pane1 = workspace.activePane - editor1 = workspace.openSingletonSync('a', split: 'right') - pane2 = workspace.activePane - expect(workspace.paneContainer.root.children).toEqual [pane1, pane2] - expect(pane2.items).toEqual [editor1] - expect(pane1.items).toEqual [] - describe "::reopenItemSync()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.activePane diff --git a/src/atom-package.coffee b/src/atom-package.coffee index b0a187a3a..ea9358670 100644 --- a/src/atom-package.coffee +++ b/src/atom-package.coffee @@ -43,11 +43,7 @@ class AtomPackage extends Package @loadStylesheets() @loadGrammars() @loadScopedProperties() - - if @metadata.activationEvents? - @registerDeferredDeserializers() - else - @requireMainModule() + @requireMainModule() unless @metadata.activationEvents? catch e console.warn "Failed to load package named '#{@name}'", e.stack ? e @@ -73,21 +69,6 @@ class AtomPackage extends Package @activationDeferred.promise - # Deprecated - activateSync: ({immediate}={}) -> - @activateResources() - if @metadata.activationEvents? and not immediate - @subscribeToActivationEvents() - else - try - @activateConfig() - @activateStylesheets() - if @requireMainModule() - @mainModule.activate(atom.packages.getPackageState(@name) ? {}) - @mainActivated = true - catch e - console.warn "Failed to activate package named '#{@name}'", e.stack - activateNow: -> try @activateConfig() @@ -228,12 +209,6 @@ class AtomPackage extends Package path.join(@path, 'index') @mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...]) - registerDeferredDeserializers: -> - for deserializerName in @metadata.deferredDeserializers ? [] - atom.deserializers.addDeferred deserializerName, => - @activateStylesheets() - @requireMainModule() - subscribeToActivationEvents: -> return unless @metadata.activationEvents? if _.isArray(@metadata.activationEvents) diff --git a/src/config.coffee b/src/config.coffee index 7896fa795..775d051ee 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -150,6 +150,14 @@ class Config toggle: (keyPath) -> @set(keyPath, !@get(keyPath)) + # Public: Restore the key path to its default value. + # + # keyPath - The {String} name of the key. + # + # Returns the new value. + restoreDefault: (keyPath) -> + @set(keyPath, _.valueForKeyPath(@defaultSettings, keyPath)) + # Public: Push the value to the array at the key path. # # keyPath - The {String} key path. diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee index 4dde07594..3c7cc2f5e 100644 --- a/src/deserializer-manager.coffee +++ b/src/deserializer-manager.coffee @@ -16,7 +16,6 @@ module.exports = class DeserializerManager constructor: -> @deserializers = {} - @deferredDeserializers = {} # Public: Register the given class(es) as deserializers. # @@ -24,13 +23,6 @@ class DeserializerManager add: (classes...) -> @deserializers[klass.name] = klass for klass in classes - # Public: Add a deferred deserializer for the given class name. - # - # name - The {String} name of the deserializer. - # fn - The {Function} that creates the deserializer. - addDeferred: (name, fn) -> - @deferredDeserializers[name] = fn - # Public: Remove the given class(es) as deserializers. # # classes - One or more classes to remove. @@ -59,8 +51,4 @@ class DeserializerManager return unless state? name = state.get?('deserializer') ? state.deserializer - if @deferredDeserializers[name] - @deferredDeserializers[name]() - delete @deferredDeserializers[name] - @deserializers[name] diff --git a/src/editor-view.coffee b/src/editor-view.coffee index 19636802a..a666c653c 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -26,6 +26,7 @@ module.exports = class EditorView extends View @characterWidthCache: {} @configDefaults: + fontFamily: '' fontSize: 20 showInvisibles: false showIndentGuide: false diff --git a/src/editor.coffee b/src/editor.coffee index 3ce83a16e..adfcd2c65 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -621,7 +621,7 @@ class Editor extends Model largestFoldStartingAtScreenRow: (screenRow) -> @displayBuffer.largestFoldStartingAtScreenRow(screenRow) - # Public: Moves the selected line up one row. + # Public: Moves the selected lines up one screen row. moveLineUp: -> selection = @getSelectedBufferRange() return if selection.start.row is 0 @@ -633,29 +633,47 @@ class Editor extends Model rows = [selection.start.row..selection.end.row] if selection.start.row isnt selection.end.row and selection.end.column is 0 rows.pop() unless @isFoldedAtBufferRow(selection.end.row) + + # Move line around the fold that is directly above the selection + precedingScreenRow = @screenPositionForBufferPosition([selection.start.row]).translate([-1]) + precedingBufferRow = @bufferPositionForScreenPosition(precedingScreenRow).row + if fold = @largestFoldContainingBufferRow(precedingBufferRow) + insertDelta = fold.getBufferRange().getRowCount() + else + insertDelta = 1 + for row in rows - screenRow = @screenPositionForBufferPosition([row]).row - if @isFoldedAtScreenRow(screenRow) - bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) + if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) + bufferRange = fold.getBufferRange() startRow = bufferRange.start.row - endRow = bufferRange.end.row - 1 - foldedRows.push(endRow - 1) + endRow = bufferRange.end.row + foldedRows.push(startRow - insertDelta) else startRow = row endRow = row + insertPosition = Point.fromObject([startRow - insertDelta]) endPosition = Point.min([endRow + 1], @buffer.getEofPosition()) lines = @buffer.getTextInRange([[startRow], endPosition]) if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row) lines = "#{lines}\n" + @buffer.deleteRows(startRow, endRow) - @buffer.insert([startRow - 1], lines) - @foldBufferRow(foldedRow) for foldedRow in foldedRows + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) + @destroyFoldsContainingBufferRow(insertPosition.row) + foldedRows.push(insertPosition.row + endRow - startRow + fold.getBufferRange().getRowCount()) - @setSelectedBufferRange(selection.translate([-1]), preserveFolds: true) + @buffer.insert(insertPosition, lines) - # Public: Moves the selected line down one row. + # Restore folds that existed before the lines were moved + for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() + @foldBufferRow(foldedRow) + + @setSelectedBufferRange(selection.translate([-insertDelta]), preserveFolds: true) + + # Public: Moves the selected lines down one screen row. moveLineDown: -> selection = @getSelectedBufferRange() lastRow = @buffer.getLastRow() @@ -667,13 +685,21 @@ class Editor extends Model rows = [selection.end.row..selection.start.row] if selection.start.row isnt selection.end.row and selection.end.column is 0 rows.shift() unless @isFoldedAtBufferRow(selection.end.row) + + # Move line around the fold that is directly below the selection + followingScreenRow = @screenPositionForBufferPosition([selection.end.row]).translate([1]) + followingBufferRow = @bufferPositionForScreenPosition(followingScreenRow).row + if fold = @largestFoldContainingBufferRow(followingBufferRow) + insertDelta = fold.getBufferRange().getRowCount() + else + insertDelta = 1 + for row in rows - screenRow = @screenPositionForBufferPosition([row]).row - if @isFoldedAtScreenRow(screenRow) - bufferRange = @bufferRangeForScreenRange([[screenRow], [screenRow + 1]]) + if fold = @displayBuffer.largestFoldStartingAtBufferRow(row) + bufferRange = fold.getBufferRange() startRow = bufferRange.start.row - endRow = bufferRange.end.row - 1 - foldedRows.push(endRow + 1) + endRow = bufferRange.end.row + foldedRows.push(endRow + insertDelta) else startRow = row endRow = row @@ -684,14 +710,23 @@ class Editor extends Model endPosition = [endRow + 1] lines = @buffer.getTextInRange([[startRow], endPosition]) @buffer.deleteRows(startRow, endRow) - insertPosition = Point.min([startRow + 1], @buffer.getEofPosition()) + + insertPosition = Point.min([startRow + insertDelta], @buffer.getEofPosition()) if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0 lines = "\n#{lines}" + + # Make sure the inserted text doesn't go into an existing fold + if fold = @displayBuffer.largestFoldStartingAtBufferRow(insertPosition.row) + @destroyFoldsContainingBufferRow(insertPosition.row) + foldedRows.push(insertPosition.row + fold.getBufferRange().getRowCount()) + @buffer.insert(insertPosition, lines) - @foldBufferRow(foldedRow) for foldedRow in foldedRows + # Restore folds that existed before the lines were moved + for foldedRow in foldedRows when 0 <= foldedRow <= @getLastBufferRow() + @foldBufferRow(foldedRow) - @setSelectedBufferRange(selection.translate([1]), preserveFolds: true) + @setSelectedBufferRange(selection.translate([insertDelta]), preserveFolds: true) # Public: Duplicates the current line. # diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index 710d7e377..82e8512a7 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -11,9 +11,8 @@ fs = require 'fs-plus' # An instance of this class is always available as the `atom.menu` global. module.exports = class MenuManager - pendingUpdateOperation: null - constructor: ({@resourcePath}) -> + @pendingUpdateOperation = null @template = [] atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems() @@ -32,7 +31,8 @@ class MenuManager # items - An {Array} of menu item {Object}s containing the keys: # :label - The {String} menu label. # :submenu - An optional {Array} of sub menu items. - # :command - An option {String} command to trigger when the item is clicked. + # :command - An optional {String} command to trigger when the item is + # clicked. # # Returns nothing. add: (items) -> @@ -48,14 +48,21 @@ class MenuManager includeSelector: (selector) -> return true if document.body.webkitMatchesSelector(selector) - # Simulate an .editor element attached to a body element that has the same - # classes as the current body element. + # Simulate an .editor element attached to a .workspace element attached to + # a body element that has the same classes as the current body element. unless @testEditor? + testBody = document.createElement('body') + testBody.classList.add(@classesForElement(document.body)...) + + testWorkspace = document.createElement('body') + workspaceClasses = @classesForElement(document.body.querySelector('.workspace')) ? ['.workspace'] + testWorkspace.classList.add(workspaceClasses...) + + testBody.appendChild(testWorkspace) + @testEditor = document.createElement('div') @testEditor.classList.add('editor') - testBody = document.createElement('body') - testBody.classList.add(document.body.classList.toString().split(' ')...) - testBody.appendChild(@testEditor) + testWorkspace.appendChild(@testEditor) @testEditor.webkitMatchesSelector(selector) @@ -109,3 +116,7 @@ class MenuManager label.replace(/\&/g, '') else label + + # Get an {Array} of {String} classes for the given element. + classesForElement: (element) -> + element?.classList.toString().split(' ') ? [] diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 03052512f..52ea5fedc 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -81,26 +81,15 @@ class PackageManager @observeDisabledPackages() # Activate a single package by name - activatePackage: (name, options={}) -> - if options.sync? or options.immediate? - return @activatePackageSync(name, options) - + activatePackage: (name) -> if pack = @getActivePackage(name) Q(pack) else pack = @loadPackage(name) - pack.activate(options).then => + pack.activate().then => @activePackages[pack.name] = pack pack - # Deprecated - activatePackageSync: (name, options) -> - return pack if pack = @getActivePackage(name) - if pack = @loadPackage(name) - @activePackages[pack.name] = pack - pack.activateSync(options) - pack - # Deactivate all packages deactivatePackages: -> @deactivatePackage(pack.name) for pack in @getLoadedPackages() diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 111e52510..09cc2a62b 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -27,7 +27,7 @@ class PaneView extends View 'destroyItem', 'destroyItems', 'destroyActiveItem', 'destroyInactiveItems', 'saveActiveItem', 'saveActiveItemAs', 'saveItem', 'saveItemAs', 'saveItems', 'itemForUri', 'activateItemForUri', 'promptToSaveItem', 'copyActiveItem', 'isActive', - 'activate', toProperty: 'model' + 'activate', 'getActiveItem', toProperty: 'model' previousActiveItem: null diff --git a/src/pane.coffee b/src/pane.coffee index a3d2be56c..2e320a3c6 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -3,6 +3,7 @@ {Model, Sequence} = require 'theorist' Serializable = require 'serializable' PaneAxis = require './pane-axis' +Editor = require './editor' PaneView = null # Public: A container for multiple items, one of which is *active* at a given @@ -85,6 +86,17 @@ class Pane extends Model getItems: -> @items.slice() + # Public: Get the active pane item in this pane. + # + # Returns a pane item. + getActiveItem: -> + @activeItem + + # Public: Returns an {Editor} if the pane item is an {Editor}, or null + # otherwise. + getActiveEditor: -> + @activeItem if @activeItem instanceof Editor + # Public: Returns the item at the specified index. itemAtIndex: (index) -> @items[index] @@ -105,15 +117,15 @@ class Pane extends Model else @activateItemAtIndex(@items.length - 1) - # Public: Returns the index of the current active item. + # Returns the index of the current active item. getActiveItemIndex: -> @items.indexOf(@activeItem) - # Public: Makes the item at the given index active. + # Makes the item at the given index active. activateItemAtIndex: (index) -> @activateItem(@itemAtIndex(index)) - # Public: Makes the given item active, adding the item if necessary. + # Makes the given item active, adding the item if necessary. activateItem: (item) -> if item? @addItem(item) diff --git a/src/project.coffee b/src/project.coffee index 55bf2cf30..f69af88f5 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -137,9 +137,8 @@ class Project extends Model # # Returns a promise that resolves to an {Editor}. open: (filePath, options={}) -> - filePath = @resolve(filePath) - resource = null - _.find @openers, (opener) -> resource = opener(filePath, options) + filePath = @resolve(filePath) ? '' + resource = opener(filePath, options) for opener in @openers when !resource if resource Q(resource) @@ -149,11 +148,10 @@ class Project extends Model # Only be used in specs openSync: (filePath, options={}) -> - filePath = @resolve(filePath) - for opener in @openers - return resource if resource = opener(filePath, options) + filePath = @resolve(filePath) ? '' + resource = opener(filePath, options) for opener in @openers when !resource - @buildEditorForBuffer(@bufferForPathSync(filePath), options) + resource or @buildEditorForBuffer(@bufferForPathSync(filePath), options) # Public: Retrieves all {Editor}s for all open files. # diff --git a/src/text-buffer.coffee b/src/text-buffer.coffee index e58ed81de..c884e7e0e 100644 --- a/src/text-buffer.coffee +++ b/src/text-buffer.coffee @@ -165,14 +165,14 @@ class TextBuffer extends TextBufferCore # Sets the path for the file. # - # path - A {String} representing the new file path - setPath: (path) -> - return if path == @getPath() + # filePath - A {String} representing the new file path + setPath: (filePath) -> + return if filePath == @getPath() @file?.off() - if path - @file = new File(path) + if filePath + @file = new File(filePath) @subscribeToFile() else @file = null @@ -188,14 +188,15 @@ class TextBuffer extends TextBufferCore # Saves the buffer at a specific path. # - # path - The path to save at. - saveAs: (path) -> - unless path then throw new Error("Can't save buffer with no file path") + # filePath - The path to save at. + saveAs: (filePath) -> + unless filePath then throw new Error("Can't save buffer with no file path") @emit 'will-be-saved', this - @setPath(path) + @setPath(filePath) @file.write(@getText()) @cachedDiskContents = @getText() + @conflict = false @emitModifiedStatusChanged(false) @emit 'saved', this @@ -212,7 +213,10 @@ class TextBuffer extends TextBufferCore else not @isEmpty() - # Identifies if a buffer is in a git conflict with `HEAD`. + # Is the buffer's text in conflict with the text on disk? + # + # This occurs when the buffer's file changes on disk while the buffer has + # unsaved changes. # # Returns a {Boolean}. isInConflict: -> @conflict diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 6b62a7829..acf631a81 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -117,6 +117,7 @@ class WorkspaceView extends View @command 'window:run-package-specs', => ipc.sendChannel('run-package-specs', path.join(atom.project.getPath(), 'spec')) @command 'window:increase-font-size', => @increaseFontSize() @command 'window:decrease-font-size', => @decreaseFontSize() + @command 'window:reset-font-size', => @model.resetFontSize() @command 'window:focus-next-pane', => @focusNextPane() @command 'window:focus-previous-pane', => @focusPreviousPane() @@ -229,9 +230,13 @@ class WorkspaceView extends View @horizontal.append(element) # Public: Returns the currently focused {PaneView}. - getActivePane: -> + getActivePaneView: -> @panes.getActivePane() + # Deprecated: Returns the currently focused {PaneView}. + getActivePane: -> + @getActivePaneView() + # Public: Returns the currently focused item from within the focused {PaneView} getActivePaneItem: -> @model.activePaneItem diff --git a/src/workspace.coffee b/src/workspace.coffee index ec5b8fa87..945ee9b6c 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -52,27 +52,43 @@ class Workspace extends Model # # filePath - A {String} file path. # options - An options {Object} (default: {}). - # :initialLine - The buffer line number to open to. + # :initialLine - A {Number} indicating which line number to open to. + # :split - A {String} ('left' or 'right') that opens the filePath in a new + # pane or an existing one if it exists. + # :changeFocus - A {Boolean} that allows the filePath to be opened without + # changing focus. + # :searchAllPanes - A {Boolean} that will open existing editors from any pane + # if the filePath is already open (default: false) # # Returns a promise that resolves to the {Editor} for the file URI. open: (filePath, options={}) -> changeFocus = options.changeFocus ? true filePath = atom.project.resolve(filePath) initialLine = options.initialLine - activePane = @activePane + searchAllPanes = options.searchAllPanes + split = options.split + uri = atom.project.relativize(filePath) - editor = activePane.itemForUri(atom.project.relativize(filePath)) if activePane and filePath - promise = atom.project.open(filePath, {initialLine}) if not editor + pane = switch split + when 'left' + @activePane.findLeftmostSibling() + when 'right' + @activePane.findOrCreateRightmostSibling() + else + if searchAllPanes + @paneContainer.paneForUri(uri) ? @activePane + else + @activePane - Q(editor ? promise) + Q(pane.itemForUri(uri) ? atom.project.open(filePath, options)) .then (editor) => - if not activePane - activePane = new Pane(items: [editor]) - @paneContainer.root = activePane + if not pane + pane = new Pane(items: [editor]) + @paneContainer.root = pane @itemOpened(editor) - activePane.activateItem(editor) - activePane.activate() if changeFocus + pane.activateItem(editor) + pane.activate() if changeFocus @emit "uri-opened" editor .catch (error) -> @@ -95,8 +111,7 @@ class Workspace extends Model @activePane.activate() if activatePane editor - # Public: Synchronously open an editor for the given URI or activate an existing - # editor in any pane if one already exists. + # Deprecated openSingletonSync: (uri, options={}) -> {initialLine, split} = options # TODO: Remove deprecated changeFocus option @@ -140,6 +155,11 @@ class Workspace extends Model destroyActivePane: -> @activePane?.destroy() + # Public: Returns an {Editor} if the active pane item is an {Editor}, + # or null otherwise. + getActiveEditor: -> + @activePane?.getActiveEditor() + increaseFontSize: -> atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1) @@ -147,6 +167,9 @@ class Workspace extends Model fontSize = atom.config.get("editor.fontSize") atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1 + resetFontSize: -> + atom.config.restoreDefault("editor.fontSize") + # Removes the item's uri from the list of potential items to reopen. itemOpened: (item) -> if uri = item.getUri?() diff --git a/static/jasmine.less b/static/jasmine.less index 7c2084807..432f44c0f 100644 --- a/static/jasmine.less +++ b/static/jasmine.less @@ -1,171 +1,164 @@ -@import "octicon-mixins.less"; +@import "octicon-mixins"; -@font-face { .octicon-font(); } +#jasmine_content { + position: fixed; + right: 100%; +} body { - background-color: #ddd; + background-color: #fff; padding: 0; } -.spec-popup { - position: absolute; - background-color: #ddd; - border: 2px solid black; - padding: 5px; - font-size: 13px; - display: none; -} - -.list-unstyled { - list-style: none; -} - -#HTMLReporter { font-size: 11px; font-family: Monaco, Consolas, monospace; line-height: 1.6em; color: #333333; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } - -#HTMLReporter .symbolHeader { - background-color: #222; - color: #c2c2c2; - font-size: 12px; - margin: 0; - padding: 5px 2px 2px 2px; -} - -#HTMLReporter .symbolSummary { - background-color: #222; - overflow: hidden; - margin: 0; -} - -#HTMLReporter .symbolSummary li { - float: left; - line-height: 10px; - height: 10px; - width: 10px; - font-size: 10px; -} - -#HTMLReporter .symbolSummary li.passed { color: #63AD75 } -#HTMLReporter .symbolSummary li.failed { color: #FF3F05 } -#HTMLReporter .symbolSummary li.skipped { color: #444 } -#HTMLReporter .symbolSummary li.pending { color: #111 } - -#HTMLReporter .symbolSummary li:before { content: "\02022"; } - -#HTMLReporter .symbolSummary li:hover { - color: white; -} - -#HTMLReporter .status { - font-size: 20px; - color: #222; - background-color: #E5FFC0; - line-height: 2em; - padding: 2px; - border: 2px solid #222; - border-left: 0; - border-right: 0; - text-align: center; -} - -#HTMLReporter .status.failed { - color: white; - background-color: rgba(204,51,63,1.0); -} - -#HTMLReporter .status .spec-count { - float: left; -} - -#HTMLReporter .status .message { -} - -#HTMLReporter .status .time { - float: right; -} - -#HTMLReporter .results .suite + .suite, #HTMLReporter .results .spec + .spec { - border-radius: 0; -} - -#HTMLReporter .results .suite, #HTMLReporter .results .spec { - border: 2px solid #222; - border-radius: 7px 0 0 0; - border-right: none; - border-bottom: none; - padding: 5px; - padding-right: 0; - padding-bottom: 0; -} - -#HTMLReporter .results .suite:first-child { - border-radius: 0; - border: 2px solid #6A4A3C; - border-top: 0; - border-left: 0; - border-right: 0; -} - -#HTMLReporter .results .suite { - border: 2px solid #6A4A3C; - border-radius: 7px 0 0 0; - border-right: none; - border-bottom: none; - padding: 5px; - padding-right: 0; - padding-bottom: 0; - background-color:rgba(204,51,63,0.33); -} - -#HTMLReporter .results .spec { - padding: 10px; - background-color:rgba(204,51,63,1.0); -} - -#HTMLReporter .results .suite > .suite, #HTMLReporter .results .suite > .spec { - margin-left: 5px -} - -#HTMLReporter .results .description { - color: white; - font-size: 15px; - padding-bottom: 10px -} - -#HTMLReporter .results .spec .spec-toggle { - font-family: Octicons Regular; - color: white; - font-size: 20px; - float: right; - cursor: pointer; - text-shadow: 3px 3px #222; - opacity: 0; -} - -#HTMLReporter .results .spec .spec-toggle:hover { - text-shadow: none; - padding-top: 3px; -} - -#HTMLReporter .results .spec:hover .spec-toggle { - opacity: 1; -} - -#HTMLReporter .resultMessage { - padding-top: 5px; - color: #fff; - font-size: 15px -} - -#HTMLReporter .stackTrace { - font-size: 12px; - padding: 5px; - margin: 5px 0 0 0; - border-radius: 2px; - line-height: 18px; - color: #666666; - border: 1px solid #ddd; - background: white; - white-space: pre; - overflow: auto; +.spec-reporter { + font-size: 11px; + line-height: 1.6em; + color: #333; + + .list-unstyled { + list-style: none; + } + + .symbol-header { + font-size: 18px; + font-weight: bold; + padding-bottom: 10px; + } + + .symbol-area { + padding: 10px; + } + + .symbol-summary { + overflow: hidden; + margin: 0; + + li { + font-family: Monaco, Consolas, monospace; + float: left; + line-height: 10px; + height: 10px; + width: 10px; + font-size: 10px; + + &.passed { + color: #5cb85c; + } + + &.failed { + color: #d9534f; + } + + &.skipped { + color: #f0ad4e; + } + + &.pending { + color: #eee; + } + + &:before { + content: "\02022"; + } + } + } + + .status { + font-size: 20px; + line-height: 2em; + padding: 5px; + border-radius: 0; + text-align: center; + + .spec-count { + float: left; + } + + .time { + float: right; + } + } + + .results { + padding: 10px; + + .description { + font-size: 16px; + padding: 5px 0 5px 0; + } + + > .suite { + > .description { + font-size: 18px; + font-weight: bold; + } + + margin-bottom: 20px; + } + + .spec { + margin-top: 5px; + padding: 0 10px 10px 10px; + border-left: 3px solid #d9534f; + + .spec-toggle { + .octicon(fold); + float: right; + cursor: pointer; + opacity: 0; + color: #999; + + &.folded { + .octicon(unfold); + } + } + + .spec-toggle:hover { + color: #333; + } + + &:hover .spec-toggle { + opacity: 1; + } + } + + .suite > .suite, + .suite > .spec { + margin-left: 10px; + } + } + + .result-message { + font-size: 16px; + font-weight: bold; + color: #d9534f; + padding: 5px 0 5px 0; + } + + .stack-trace { + font-size: 12px; + margin: 5px 0 0 0; + border-radius: 2px; + line-height: 18px; + color: #666; + border: 1px solid #ddd; + overflow: auto; + } + + .tooltip { + .tooltip-inner { + border: 1px solid #ccc; + background: #fff; + color: #666; + max-width: 400px; + } + + &.in { + opacity: 1; + } + + .tooltip-arrow { + visibility: hidden; + } + } } diff --git a/vendor/apm b/vendor/apm index ce140e662..0ed2a1ef7 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit ce140e66283c07f5837dd999e75580f3757a249a +Subproject commit 0ed2a1ef753c65361314a5fb3934338d068e8821