From 8a5dfeae05a27170ecec689b26c621ac73a85946 Mon Sep 17 00:00:00 2001 From: George Ogata Date: Wed, 24 Jun 2015 00:14:10 -0400 Subject: [PATCH 1/9] Add Pane::onWillRemoveItem event. --- spec/pane-spec.coffee | 13 +++++++++++++ src/pane.coffee | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index ba2f02802..6ee7d1bab 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -209,6 +209,12 @@ describe "Pane", -> expect(item2.isDestroyed()).toBe true expect(events).toEqual [{item: item2, index: 1}] + it "invokes ::onWillRemoveItem() observers", -> + events = [] + pane.onWillRemoveItem (event) -> events.push(event) + pane.destroyItem(item2) + expect(events).toEqual [{item: item2, index: 1, destroyed: true}] + it "invokes ::onDidRemoveItem() observers", -> events = [] pane.onDidRemoveItem (event) -> events.push(event) @@ -496,6 +502,13 @@ describe "Pane", -> expect(pane1.getItems()).toEqual [item1, item3] expect(pane2.getItems()).toEqual [item4, item2, item5] + it "invokes ::onWillRemoveItem() observers", -> + events = [] + pane1.onWillRemoveItem (event) -> events.push(event) + pane1.moveItemToPane(item2, pane2, 1) + + expect(events).toEqual [{item: item2, index: 1, destroyed: false}] + it "invokes ::onDidRemoveItem() observers", -> events = [] pane1.onDidRemoveItem (event) -> events.push(event) diff --git a/src/pane.coffee b/src/pane.coffee index 477239bdd..53980c8d1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -163,6 +163,15 @@ class Pane extends Model onDidRemoveItem: (callback) -> @emitter.on 'did-remove-item', callback + # Public: Invoke the given callback before an item is removed from the pane. + # + # * `callback` {Function} to be called with when items are removed. + # * `event` {Object} with the following keys: + # * `item` The pane item to be removed. + # * `index` {Number} indicating where the item is located. + onWillRemoveItem: (callback) -> + @emitter.on 'will-remove-item', callback + # Public: Invoke the given callback when an item is moved within the pane. # # * `callback` {Function} to be called with when items are moved. @@ -358,6 +367,8 @@ class Pane extends Model index = @items.indexOf(item) return if index is -1 + @emitter.emit 'will-remove-item', {item, index, destroyed} + if Grim.includeDeprecatedAPIs and typeof item.on is 'function' @unsubscribe item @unsubscribeFromItem(item) From 3ac69500b0627067db59ea60f02f456fee6791be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Machist=C3=A9=20N=2E=20Quintana?= Date: Tue, 30 Jun 2015 08:24:04 -0400 Subject: [PATCH 2/9] Add SF UI font --- static/variables/ui-variables.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/variables/ui-variables.less b/static/variables/ui-variables.less index d386730db..aa33d0e06 100644 --- a/static/variables/ui-variables.less +++ b/static/variables/ui-variables.less @@ -82,4 +82,4 @@ // Other -@font-family: 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif; +@font-family: 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif; From 5d957b677bd91e87f8f2f61092dd5380db1a5b00 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Jun 2015 11:58:45 -0500 Subject: [PATCH 3/9] Revert "Merge pull request #7373 from atom/revert-7350-ns-assertions" This reverts commit 1a3441a503874c67085221b8e227bb01216dd282, reversing changes made to 1aab5e74d962bb6160a9844300c596234940a169. --- package.json | 2 +- spec/atom-spec.coffee | 74 ++++++++++++++++++++++++++++++------- src/atom.coffee | 57 ++++++++++++++++++---------- src/tokenized-buffer.coffee | 12 +++++- 4 files changed, 110 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 8cca44bf7..3431c49bb 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "link": "0.30.0", "markdown-preview": "0.150.0", "metrics": "0.51.0", - "notifications": "0.56.0", + "notifications": "0.57.0", "open-on-github": "0.37.0", "package-generator": "0.39.0", "release-notes": "0.53.0", diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index 99a7f432e..c8c4a598f 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -86,13 +86,13 @@ describe "the `atom` global", -> error = e window.onerror.call(window, e.toString(), 'abc', 2, 3, e) - delete willThrowSpy.mostRecentCall.args[0].preventDefault - expect(willThrowSpy).toHaveBeenCalledWith - message: error.toString() - url: 'abc' - line: 2 - column: 3 - originalError: error + expect(willThrowSpy).toHaveBeenCalledWith(error) + + # Deprecated event properties + expect(error.url).toBe 'abc' + expect(error.line).toBe 2 + expect(error.column).toBe 3 + expect(error.originalError).toBe error it "will not show the devtools when preventDefault() is called", -> willThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault() @@ -120,12 +120,60 @@ describe "the `atom` global", -> catch e error = e window.onerror.call(window, e.toString(), 'abc', 2, 3, e) - expect(didThrowSpy).toHaveBeenCalledWith - message: error.toString() - url: 'abc' - line: 2 - column: 3 - originalError: error + + expect(didThrowSpy).toHaveBeenCalledWith(error) + + # Deprecated event properties + expect(error.url).toBe 'abc' + expect(error.line).toBe 2 + expect(error.column).toBe 3 + expect(error.originalError).toBe error + + it "will not show the devtools when preventDefault() is called", -> + didThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault() + atom.onDidThrowError(didThrowSpy) + + try + a + 1 + catch e + window.onerror.call(window, e.toString(), 'abc', 2, 3, e) + + expect(didThrowSpy).toHaveBeenCalled() + expect(atom.openDevTools).not.toHaveBeenCalled() + expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled() + + describe ".assert(condition, message, metadata)", -> + errors = null + + beforeEach -> + errors = [] + atom.onDidFailAssertion (error) -> errors.push(error) + + describe "if the condition is false", -> + it "notifies onDidFailAssertion handlers with an error object based on the call site of the assertion", -> + result = atom.assert(false, "a == b") + expect(result).toBe false + expect(errors.length).toBe 1 + expect(errors[0].message).toBe "Assertion failed: a == b" + expect(errors[0].stack).toContain('atom-spec') + + describe "if metadata is an object", -> + it "assigns the object on the error as `metadata`", -> + metadata = {foo: 'bar'} + atom.assert(false, "a == b", metadata) + expect(errors[0].metadata).toBe metadata + + describe "if metadata is a function", -> + it "assigns the function's return value on the error as `metadata`", -> + metadata = {foo: 'bar'} + atom.assert(false, "a == b", -> metadata) + expect(errors[0].metadata).toBe metadata + + describe "if the condition is true", -> + it "does nothing", -> + result = atom.assert(true, "a == b") + expect(result).toBe true + expect(errors).toEqual [] describe "saving and loading", -> afterEach -> atom.mode = "spec" diff --git a/src/atom.coffee b/src/atom.coffee index 316c51e29..0704f0bfb 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -198,28 +198,31 @@ class Atom extends Model initialize: -> sourceMapCache = {} - window.onerror = => - @lastUncaughtError = Array::slice.call(arguments) - [message, url, line, column, originalError] = @lastUncaughtError + window.onerror = (message, url, line, column, error) => + @lastUncaughtError = error - convertedLine = convertLine(url, line, column, sourceMapCache) - {line, column} = convertedLine if convertedLine? - originalError.stack = convertStackTrace(originalError.stack, sourceMapCache) if originalError - - eventObject = {message, url, line, column, originalError} + # TODO: These should be deprecated for 2.0 once we transition the + # exception-reporting package to the new API. + error ?= {} + error.message ?= message + error.url = url + error.line = line + error.column = column + error.originalError = error openDevTools = true - eventObject.preventDefault = -> openDevTools = false + error.preventDefault = -> openDevTools = false - @emitter.emit 'will-throw-error', eventObject + # TODO: Deprecate onWillThrowError once we transition the notifications + # package to use onDidThrowError instead. + @emitter.emit 'will-throw-error', error + @emit 'uncaught-error', arguments... if includeDeprecatedAPIs + @emitter.emit 'did-throw-error', error if openDevTools @openDevTools() @executeJavaScriptInDevTools('DevToolsAPI.showConsole()') - @emit 'uncaught-error', arguments... if includeDeprecatedAPIs - @emitter.emit 'did-throw-error', {message, url, line, column, originalError} - @disposables?.dispose() @disposables = new CompositeDisposable @@ -321,18 +324,16 @@ class Atom extends Model # Extended: Invoke the given callback whenever there is an unhandled error. # - # * `callback` {Function} to be called whenever there is an unhandled error - # * `event` {Object} - # * `originalError` {Object} the original error object - # * `message` {String} the original error object - # * `url` {String} Url to the file where the error originated. - # * `line` {Number} - # * `column` {Number} + # * `callback` {Function} to be called whenever there is an unhandled error. + # * `error` The unhandled {Error} object. # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidThrowError: (callback) -> @emitter.on 'did-throw-error', callback + onDidFailAssertion: (callback) -> + @emitter.on 'did-fail-assertion', callback + ### Section: Atom Details ### @@ -715,6 +716,22 @@ class Atom extends Model Section: Private ### + assert: (condition, message, metadata) -> + return true if condition + + error = new Error("Assertion failed: " + message) + Error.captureStackTrace(error, @assert) + + if metadata? + if typeof metadata is 'function' + error.metadata = metadata() + else + error.metadata = metadata + + @emitter.emit 'did-fail-assertion', error + + false + deserializeProject: -> Project = require './project' diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index c764c1a1f..5919841f5 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -292,7 +292,17 @@ class TokenizedBuffer extends Model # Returns a {Boolean} indicating whether the given buffer row starts # a a foldable row range due to the code's indentation patterns. isFoldableCodeAtRow: (row) -> - return false if @buffer.isRowBlank(row) or @tokenizedLineForRow(row).isComment() + # Investigating an exception that's occurring here due to the line being + # undefined. This should paper over the problem but we want to figure out + # what is happening: + tokenizedLine = @tokenizedLineForRow(row) + atom.assert tokenizedLine?, "TokenizedLine is defined", => + metadata: + row: row + rowCount: @tokenizedLines.length + return false unless tokenizedLine? + + return false if @buffer.isRowBlank(row) or tokenizedLine.isComment() nextRow = @buffer.nextNonBlankRow(row) return false unless nextRow? From 1d8035c3cc263d58f1bd69c6a0f4255852c06854 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Jun 2015 19:21:39 -0500 Subject: [PATCH 4/9] Back out changes to onDidThrowError API --- spec/atom-spec.coffee | 41 +++++++++++++---------------------------- src/atom.coffee | 41 +++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/spec/atom-spec.coffee b/spec/atom-spec.coffee index c8c4a598f..d2d1d68ae 100644 --- a/spec/atom-spec.coffee +++ b/spec/atom-spec.coffee @@ -86,13 +86,13 @@ describe "the `atom` global", -> error = e window.onerror.call(window, e.toString(), 'abc', 2, 3, e) - expect(willThrowSpy).toHaveBeenCalledWith(error) - - # Deprecated event properties - expect(error.url).toBe 'abc' - expect(error.line).toBe 2 - expect(error.column).toBe 3 - expect(error.originalError).toBe error + delete willThrowSpy.mostRecentCall.args[0].preventDefault + expect(willThrowSpy).toHaveBeenCalledWith + message: error.toString() + url: 'abc' + line: 2 + column: 3 + originalError: error it "will not show the devtools when preventDefault() is called", -> willThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault() @@ -120,27 +120,12 @@ describe "the `atom` global", -> catch e error = e window.onerror.call(window, e.toString(), 'abc', 2, 3, e) - - expect(didThrowSpy).toHaveBeenCalledWith(error) - - # Deprecated event properties - expect(error.url).toBe 'abc' - expect(error.line).toBe 2 - expect(error.column).toBe 3 - expect(error.originalError).toBe error - - it "will not show the devtools when preventDefault() is called", -> - didThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault() - atom.onDidThrowError(didThrowSpy) - - try - a + 1 - catch e - window.onerror.call(window, e.toString(), 'abc', 2, 3, e) - - expect(didThrowSpy).toHaveBeenCalled() - expect(atom.openDevTools).not.toHaveBeenCalled() - expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled() + expect(didThrowSpy).toHaveBeenCalledWith + message: error.toString() + url: 'abc' + line: 2 + column: 3 + originalError: error describe ".assert(condition, message, metadata)", -> errors = null diff --git a/src/atom.coffee b/src/atom.coffee index 0704f0bfb..15bd4176a 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -198,31 +198,28 @@ class Atom extends Model initialize: -> sourceMapCache = {} - window.onerror = (message, url, line, column, error) => - @lastUncaughtError = error + window.onerror = => + @lastUncaughtError = Array::slice.call(arguments) + [message, url, line, column, originalError] = @lastUncaughtError - # TODO: These should be deprecated for 2.0 once we transition the - # exception-reporting package to the new API. - error ?= {} - error.message ?= message - error.url = url - error.line = line - error.column = column - error.originalError = error + convertedLine = convertLine(url, line, column, sourceMapCache) + {line, column} = convertedLine if convertedLine? + originalError.stack = convertStackTrace(originalError.stack, sourceMapCache) if originalError + + eventObject = {message, url, line, column, originalError} openDevTools = true - error.preventDefault = -> openDevTools = false + eventObject.preventDefault = -> openDevTools = false - # TODO: Deprecate onWillThrowError once we transition the notifications - # package to use onDidThrowError instead. - @emitter.emit 'will-throw-error', error - @emit 'uncaught-error', arguments... if includeDeprecatedAPIs - @emitter.emit 'did-throw-error', error + @emitter.emit 'will-throw-error', eventObject if openDevTools @openDevTools() @executeJavaScriptInDevTools('DevToolsAPI.showConsole()') + @emit 'uncaught-error', arguments... if includeDeprecatedAPIs + @emitter.emit 'did-throw-error', {message, url, line, column, originalError} + @disposables?.dispose() @disposables = new CompositeDisposable @@ -324,13 +321,21 @@ class Atom extends Model # Extended: Invoke the given callback whenever there is an unhandled error. # - # * `callback` {Function} to be called whenever there is an unhandled error. - # * `error` The unhandled {Error} object. + # * `callback` {Function} to be called whenever there is an unhandled error + # * `event` {Object} + # * `originalError` {Object} the original error object + # * `message` {String} the original error object + # * `url` {String} Url to the file where the error originated. + # * `line` {Number} + # * `column` {Number} # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidThrowError: (callback) -> @emitter.on 'did-throw-error', callback + # TODO: Make this part of the public API. We should make onDidThrowError + # match the interface by only yielding an exception object to the handler + # and deprecating the old behavior. onDidFailAssertion: (callback) -> @emitter.on 'did-fail-assertion', callback From c9a8585b4949920532c0724810748b31b59053fb Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Jun 2015 19:22:26 -0500 Subject: [PATCH 5/9] :art: --- src/atom.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom.coffee b/src/atom.coffee index 15bd4176a..03525df68 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -724,7 +724,7 @@ class Atom extends Model assert: (condition, message, metadata) -> return true if condition - error = new Error("Assertion failed: " + message) + error = new Error("Assertion failed: #{message}") Error.captureStackTrace(error, @assert) if metadata? From fbfb18aaf67c48eb01f3b80185f14edf3edf7573 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Jun 2015 20:02:17 -0500 Subject: [PATCH 6/9] :arrow_up: exception-reporting --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3431c49bb..fa8844a92 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "deprecation-cop": "0.53.0", "dev-live-reload": "0.46.0", "encoding-selector": "0.20.0", - "exception-reporting": "0.25.0", + "exception-reporting": "0.31.0", "find-and-replace": "0.174.1", "fuzzy-finder": "0.87.0", "git-diff": "0.55.0", From 82e25872b2b87757c8cd94bfdb5a2b1f79e501c3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 30 Jun 2015 12:32:10 -0500 Subject: [PATCH 7/9] Handle indentation with mixed tabs and spaces --- spec/text-editor-spec.coffee | 11 ++++++----- src/selection.coffee | 2 +- src/text-editor.coffee | 9 +++++---- src/tokenized-buffer.coffee | 13 +++++++++---- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index f20184e01..64bb7adba 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3707,11 +3707,12 @@ describe "TextEditor", -> expect(editor.indentLevelForLine("\t\thello")).toBe(2) it "returns the indent level based on the character starting the line when the leading whitespace contains both spaces and tabs", -> - expect(editor.indentLevelForLine("\t hello")).toBe(1) - expect(editor.indentLevelForLine(" \thello")).toBe(1) - expect(editor.indentLevelForLine(" \t hello")).toBe(1) - expect(editor.indentLevelForLine(" \t \thello")).toBe(2) - expect(editor.indentLevelForLine(" \t \thello")).toBe(2.5) + expect(editor.indentLevelForLine("\t hello")).toBe(2) + expect(editor.indentLevelForLine(" \thello")).toBe(2) + expect(editor.indentLevelForLine(" \t hello")).toBe(2.5) + expect(editor.indentLevelForLine(" \t \thello")).toBe(4) + expect(editor.indentLevelForLine(" \t \thello")).toBe(4) + expect(editor.indentLevelForLine(" \t \t hello")).toBe(4.5) describe "when the buffer is reloaded", -> it "preserves the current cursor position", -> diff --git a/src/selection.coffee b/src/selection.coffee index 3f7878195..6a4d4726f 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -622,7 +622,7 @@ class Selection extends Model else currentIndentLevel = @editor.indentLevelForLine(lines[i]) indentLevel = Math.max(0, currentIndentLevel + indentAdjustment) - lines[i] = line.replace(/^(\t+| +)/, @editor.buildIndentString(indentLevel)) + lines[i] = line.replace(/^[\t ]+/, @editor.buildIndentString(indentLevel)) return # Indent the current line(s). diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 3b19970b8..37d9c2021 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2447,13 +2447,14 @@ class TextEditor extends Model options.autoIndent ?= @shouldAutoIndent() @mutateSelectedText (selection) -> selection.indent(options) - # Constructs the string used for tabs. - buildIndentString: (number, column=0) -> + # Constructs the string used for indents. + buildIndentString: (level, column=0) -> if @getSoftTabs() tabStopViolation = column % @getTabLength() - _.multiplyString(" ", Math.floor(number * @getTabLength()) - tabStopViolation) + _.multiplyString(" ", Math.floor(level * @getTabLength()) - tabStopViolation) else - _.multiplyString("\t", Math.floor(number)) + excessWhitespace = _.multiplyString(' ', Math.round((level - Math.floor(level)) * @getTabLength())) + _.multiplyString("\t", Math.floor(level)) + excessWhitespace ### Section: Grammars diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index c764c1a1f..4cea91fcd 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -411,10 +411,15 @@ class TokenizedBuffer extends Model @indentLevelForLine(line) indentLevelForLine: (line) -> - if match = line.match(/^\t+/) - match[0].length - else if match = line.match(/^ +/) - match[0].length / @getTabLength() + if match = line.match(/^[\t ]+/) + indentLength = 0 + for character in match[0] + if character is '\t' + indentLength += @getTabLength() - (indentLength % @getTabLength()) + else + indentLength++ + + indentLength / @getTabLength() else 0 From f61531c249562385d79aa2eedecc181048eefca3 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 30 Jun 2015 14:04:37 -0700 Subject: [PATCH 8/9] :arrow_up: solarized syntax themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fa8844a92..ac2d829c2 100644 --- a/package.json +++ b/package.json @@ -74,8 +74,8 @@ "one-dark-syntax": "0.7.1", "one-light-syntax": "0.7.0", "one-light-ui": "0.9.1", - "solarized-dark-syntax": "0.35.0", - "solarized-light-syntax": "0.21.0", + "solarized-dark-syntax": "0.38.1", + "solarized-light-syntax": "0.22.1", "archive-view": "0.58.0", "autocomplete-atom-api": "0.9.0", "autocomplete-css": "0.8.0", From b1d6b79ddd39c87e90706f6b0824f0eba59db20b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 30 Jun 2015 20:02:27 -0500 Subject: [PATCH 9/9] :arrow_up: exception-reporting --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac2d829c2..ac34ce8e7 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "deprecation-cop": "0.53.0", "dev-live-reload": "0.46.0", "encoding-selector": "0.20.0", - "exception-reporting": "0.31.0", + "exception-reporting": "0.32.0", "find-and-replace": "0.174.1", "fuzzy-finder": "0.87.0", "git-diff": "0.55.0",