From bc454f14e064093821e55b0d3ea9219d6c33c55d Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 14:58:36 -0800 Subject: [PATCH 01/35] Add a warning notification for oversize file open --- spec/workspace-spec.coffee | 19 +++++++++++++++++++ src/project.coffee | 4 +++- src/workspace.coffee | 8 +++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 16bcdbcab..66211e1c5 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -266,6 +266,25 @@ describe "Workspace", -> expect(deprecations[0].getStacks()[0].metadata.packageName).toBe "package-with-deprecated-pane-item-method" jasmine.restoreDeprecationsSnapshot() + describe "when there is an error opening the file", -> + notificationSpy = null + beforeEach -> + atom.notifications.onDidAddNotification notificationSpy = jasmine.createSpy() + + describe "when a large file is opened", -> + beforeEach -> + spyOn(fs, 'getSizeSync').andReturn 2 * 1048577 # 2MB + + it "creates a notification", -> + waitsForPromise -> + workspace.open('file1', workspace.getActivePane()) + + runs -> + expect(notificationSpy).toHaveBeenCalled() + notification = notificationSpy.mostRecentCall.args[0] + expect(notification.getType()).toBe 'warning' + expect(notification.getMessage()).toContain < '2MB' + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/project.coffee b/src/project.coffee index a96788a53..f972c2695 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -279,7 +279,9 @@ class Project extends Model # Returns a promise that resolves to the {TextBuffer}. buildBuffer: (absoluteFilePath) -> if fs.getSizeSync(absoluteFilePath) >= 2 * 1048576 # 2MB - throw new Error("Atom can only handle files < 2MB for now.") + error = new Error("Atom can only handle files < 2MB for now.") + error.name = 'OversizeFileError' + throw error buffer = new TextBuffer({filePath: absoluteFilePath}) buffer.setEncoding(atom.config.get('core.fileEncoding')) diff --git a/src/workspace.coffee b/src/workspace.coffee index a6637ffc3..1ba468aae 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -447,7 +447,13 @@ class Workspace extends Model if uri? item = pane.itemForURI(uri) item ?= opener(uri, options) for opener in @getOpeners() when !item - item ?= atom.project.open(uri, options) + + try + item ?= atom.project.open(uri, options) + catch error + if error.name is 'OversizeFileError' + atom.notifications.addWarning(error.message) + return Q() Q(item) .then (item) => From 11d4222c9ff5414b613ca56ca8136f92860560b1 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 15:38:51 -0800 Subject: [PATCH 02/35] =?UTF-8?q?Handle=20error=20when=20opening=20a=20fil?= =?UTF-8?q?e=20that=20doesn=E2=80=99t=20exist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/workspace-spec.coffee | 12 ++++++++++++ src/workspace.coffee | 2 ++ 2 files changed, 14 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 66211e1c5..249679242 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -285,6 +285,18 @@ describe "Workspace", -> expect(notification.getType()).toBe 'warning' expect(notification.getMessage()).toContain < '2MB' + describe "when a file does not exist", -> + it "creates a notification", -> + waitsForPromise -> + workspace.open('not-a-file.md', workspace.getActivePane()) + + runs -> + expect(notificationSpy).toHaveBeenCalled() + notification = notificationSpy.mostRecentCall.args[0] + expect(notification.getType()).toBe 'warning' + expect(notification.getMessage()).toContain < 'No such file' + expect(notification.getMessage()).toContain < 'not-a-file.md' + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/workspace.coffee b/src/workspace.coffee index 1ba468aae..bd6aee8fa 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -453,6 +453,8 @@ class Workspace extends Model catch error if error.name is 'OversizeFileError' atom.notifications.addWarning(error.message) + else if error.code is 'ENOENT' and error.path? + atom.notifications.addWarning("No such file '#{error.path}'") return Q() Q(item) From 4138b951466f94ba90f84a26573995582eaaaaf4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 15:55:27 -0800 Subject: [PATCH 03/35] Remove unnecessary < chars. wtf --- spec/workspace-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 249679242..bb5c662ba 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -283,7 +283,7 @@ describe "Workspace", -> expect(notificationSpy).toHaveBeenCalled() notification = notificationSpy.mostRecentCall.args[0] expect(notification.getType()).toBe 'warning' - expect(notification.getMessage()).toContain < '2MB' + expect(notification.getMessage()).toContain '< 2MB' describe "when a file does not exist", -> it "creates a notification", -> @@ -294,8 +294,8 @@ describe "Workspace", -> expect(notificationSpy).toHaveBeenCalled() notification = notificationSpy.mostRecentCall.args[0] expect(notification.getType()).toBe 'warning' - expect(notification.getMessage()).toContain < 'No such file' - expect(notification.getMessage()).toContain < 'not-a-file.md' + expect(notification.getMessage()).toContain 'No such file' + expect(notification.getMessage()).toContain 'not-a-file.md' describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> From ca1f66d197527e9e76bf5d84128f7c1d7bc833df Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 16:01:23 -0800 Subject: [PATCH 04/35] Post a notification when the user cannot access a file --- spec/workspace-spec.coffee | 20 ++++++++++++++++++++ src/project.coffee | 5 +++++ src/workspace.coffee | 2 ++ 3 files changed, 27 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index bb5c662ba..2c232c956 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -297,6 +297,26 @@ describe "Workspace", -> expect(notification.getMessage()).toContain 'No such file' expect(notification.getMessage()).toContain 'not-a-file.md' + describe "when the user does not have access to the file", -> + beforeEach -> + spyOn(fs, 'openSync').andCallFake (path)-> + error = new Error("EACCES, permission denied '#{path}'") + error.path = path + error.code = 'EACCES' + throw error + + it "creates a notification", -> + waitsForPromise -> + workspace.open('file1', workspace.getActivePane()) + + runs -> + expect(notificationSpy).toHaveBeenCalled() + notification = notificationSpy.mostRecentCall.args[0] + expect(notification.getType()).toBe 'warning' + expect(notification.getMessage()).toContain 'Permission denied' + expect(notification.getMessage()).toContain 'file1' + + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/project.coffee b/src/project.coffee index f972c2695..d822cbd76 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -218,6 +218,11 @@ class Project extends Model # Returns a promise that resolves to an {TextEditor}. open: (filePath, options={}) -> filePath = @resolvePath(filePath) + + # Make sure we have permissions + fileDescriptor = fs.openSync(filePath, 'r+') + fs.closeSync(fileDescriptor) + @bufferForPath(filePath).then (buffer) => @buildEditorForBuffer(buffer, options) diff --git a/src/workspace.coffee b/src/workspace.coffee index bd6aee8fa..14854a81e 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -455,6 +455,8 @@ class Workspace extends Model atom.notifications.addWarning(error.message) else if error.code is 'ENOENT' and error.path? atom.notifications.addWarning("No such file '#{error.path}'") + else if error.code is 'EACCES' and error.path? + atom.notifications.addWarning("Permission denied '#{error.path}'") return Q() Q(item) From c20d3a8182d75d8667d2c9c0a0ba80735928c3ac Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 16:04:07 -0800 Subject: [PATCH 05/35] Throw an error when the error is unhandled --- spec/workspace-spec.coffee | 8 ++++++++ src/workspace.coffee | 2 ++ 2 files changed, 10 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 2c232c956..c0910e66b 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -316,6 +316,14 @@ describe "Workspace", -> expect(notification.getMessage()).toContain 'Permission denied' expect(notification.getMessage()).toContain 'file1' + describe "when there is an unhandled error", -> + beforeEach -> + spyOn(fs, 'openSync').andCallFake (path)-> + throw new Error("I dont even know what is happening right now!!") + + it "creates a notification", -> + open = -> workspace.open('file1', workspace.getActivePane()) + expect(open).toThrow() describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> diff --git a/src/workspace.coffee b/src/workspace.coffee index 14854a81e..770b6464b 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -457,6 +457,8 @@ class Workspace extends Model atom.notifications.addWarning("No such file '#{error.path}'") else if error.code is 'EACCES' and error.path? atom.notifications.addWarning("Permission denied '#{error.path}'") + else + throw error return Q() Q(item) From 2f3ce508754b466d55238d8402e8d2932e04bdc1 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 16:26:56 -0800 Subject: [PATCH 06/35] Add large file issue link to large file error --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 770b6464b..4efd1d6e6 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -452,7 +452,7 @@ class Workspace extends Model item ?= atom.project.open(uri, options) catch error if error.name is 'OversizeFileError' - atom.notifications.addWarning(error.message) + atom.notifications.addWarning(error.message + " Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") else if error.code is 'ENOENT' and error.path? atom.notifications.addWarning("No such file '#{error.path}'") else if error.code is 'EACCES' and error.path? From d5e04e883eb110177cd1a5170e37f52a24080803 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 17:12:55 -0800 Subject: [PATCH 07/35] Use the error.code and path in the error --- spec/workspace-view-spec.coffee | 5 ++++- src/workspace.coffee | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index ade0dcc4d..e2065a661 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -318,7 +318,10 @@ describe "WorkspaceView", -> it "emits a warning notification when the user does not have permission", -> spyOn(Pane::, 'saveActiveItem').andCallFake -> - throw new Error("EACCES, permission denied '/Some/dir/and-a-file.js'") + error = new Error("EACCES, permission denied '/Some/dir/and-a-file.js'") + error.code = 'EACCES' + error.path = '/Some/dir/and-a-file.js' + throw error atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() atom.workspace.saveActivePaneItem() diff --git a/src/workspace.coffee b/src/workspace.coffee index 4efd1d6e6..3c11c7d27 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -624,8 +624,8 @@ class Workspace extends Model catch error if error.message.endsWith('is a directory') atom.notifications.addWarning("Unable to save file: #{error.message}") - else if error.message.startsWith('EACCES,') - atom.notifications.addWarning("Unable to save file: #{error.message.replace('EACCES, ', '')}") + else if error.code is 'EACCES' and error.path? + atom.notifications.addWarning("Unable to save file: Permission denied '#{error.path}'") else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message) fileName = errorMatch[1] atom.notifications.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to") From 16468eb65d14ce8ba82baeef53b753337f2b3b9e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 17:14:26 -0800 Subject: [PATCH 08/35] Move workspace specs to workspace. --- spec/workspace-spec.coffee | 40 +++++++++++++++++++++++++++++++++ spec/workspace-view-spec.coffee | 39 -------------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index c0910e66b..a016724da 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1,6 +1,7 @@ path = require 'path' temp = require 'temp' Workspace = require '../src/workspace' +Pane = require '../src/pane' {View} = require '../src/space-pen-extensions' platform = require './spec-helper-platform' _ = require 'underscore-plus' @@ -916,3 +917,42 @@ describe "Workspace", -> expect(results[0].replacements).toBe 6 expect(editor.isModified()).toBeTruthy() + + describe "::saveActivePaneItem()", -> + describe "when there is an error", -> + it "emits a warning notification when the file cannot be saved", -> + spyOn(Pane::, 'saveActiveItem').andCallFake -> + throw new Error("'/some/file' is a directory") + + atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() + atom.workspace.saveActivePaneItem() + expect(addedSpy).toHaveBeenCalled() + expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' + + it "emits a warning notification when the directory cannot be written to", -> + spyOn(Pane::, 'saveActiveItem').andCallFake -> + throw new Error("ENOTDIR, not a directory '/Some/dir/and-a-file.js'") + + atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() + atom.workspace.saveActivePaneItem() + expect(addedSpy).toHaveBeenCalled() + expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' + + it "emits a warning notification when the user does not have permission", -> + spyOn(Pane::, 'saveActiveItem').andCallFake -> + error = new Error("EACCES, permission denied '/Some/dir/and-a-file.js'") + error.code = 'EACCES' + error.path = '/Some/dir/and-a-file.js' + throw error + + atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() + atom.workspace.saveActivePaneItem() + expect(addedSpy).toHaveBeenCalled() + expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' + + it "emits a warning notification when the file cannot be saved", -> + spyOn(Pane::, 'saveActiveItem').andCallFake -> + throw new Error("no one knows") + + save = -> atom.workspace.saveActivePaneItem() + expect(save).toThrow() diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index e2065a661..7368efe79 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -295,42 +295,3 @@ describe "WorkspaceView", -> modalContainer = workspaceElement.querySelector('atom-panel-container.modal') expect(modalContainer.parentNode).toBe workspaceElement - - describe "::saveActivePaneItem()", -> - describe "when there is an error", -> - it "emits a warning notification when the file cannot be saved", -> - spyOn(Pane::, 'saveActiveItem').andCallFake -> - throw new Error("'/some/file' is a directory") - - atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() - atom.workspace.saveActivePaneItem() - expect(addedSpy).toHaveBeenCalled() - expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' - - it "emits a warning notification when the directory cannot be written to", -> - spyOn(Pane::, 'saveActiveItem').andCallFake -> - throw new Error("ENOTDIR, not a directory '/Some/dir/and-a-file.js'") - - atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() - atom.workspace.saveActivePaneItem() - expect(addedSpy).toHaveBeenCalled() - expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' - - it "emits a warning notification when the user does not have permission", -> - spyOn(Pane::, 'saveActiveItem').andCallFake -> - error = new Error("EACCES, permission denied '/Some/dir/and-a-file.js'") - error.code = 'EACCES' - error.path = '/Some/dir/and-a-file.js' - throw error - - atom.notifications.onDidAddNotification addedSpy = jasmine.createSpy() - atom.workspace.saveActivePaneItem() - expect(addedSpy).toHaveBeenCalled() - expect(addedSpy.mostRecentCall.args[0].getType()).toBe 'warning' - - it "emits a warning notification when the file cannot be saved", -> - spyOn(Pane::, 'saveActiveItem').andCallFake -> - throw new Error("no one knows") - - save = -> atom.workspace.saveActivePaneItem() - expect(save).toThrow() From 8435826e8ad94b2697f34520c9e935394c1fa9c9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 7 Jan 2015 17:18:39 -0800 Subject: [PATCH 09/35] Remove linter errors --- spec/workspace-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index a016724da..6c84c8f9b 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -300,7 +300,7 @@ describe "Workspace", -> describe "when the user does not have access to the file", -> beforeEach -> - spyOn(fs, 'openSync').andCallFake (path)-> + spyOn(fs, 'openSync').andCallFake (path) -> error = new Error("EACCES, permission denied '#{path}'") error.path = path error.code = 'EACCES' @@ -319,7 +319,7 @@ describe "Workspace", -> describe "when there is an unhandled error", -> beforeEach -> - spyOn(fs, 'openSync').andCallFake (path)-> + spyOn(fs, 'openSync').andCallFake (path) -> throw new Error("I dont even know what is happening right now!!") it "creates a notification", -> From b8efbedee19d08c72f890c67e4d192a4c151359f Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 8 Jan 2015 17:10:59 -0800 Subject: [PATCH 10/35] Create a warning notification when buffer has a watch error --- spec/project-spec.coffee | 19 +++++++++++++++++++ src/project.coffee | 19 ++++++++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index ba91fbde7..930b4a849 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -51,6 +51,25 @@ describe "Project", -> editor.saveAs(tempFile) expect(atom.project.getPaths()[0]).toBe path.dirname(tempFile) + describe "when a watch error is thrown from the TextBuffer", -> + editor = null + beforeEach -> + waitsForPromise -> + atom.project.open(require.resolve('./fixtures/dir/a')).then (o) -> editor = o + + it "creates a warning notification", -> + atom.notifications.onDidAddNotification noteSpy = jasmine.createSpy() + + editor.buffer.emitter.emit 'will-throw-watch-error', + handle: jasmine.createSpy() + error: new Error('SomeError') + + expect(noteSpy).toHaveBeenCalled() + + notification = noteSpy.mostRecentCall.args[0] + expect(notification.getType()).toBe 'warning' + expect(notification.getDetail()).toBe 'SomeError' + describe ".open(path)", -> [absolutePath, newBufferHandler] = [] diff --git a/src/project.coffee b/src/project.coffee index d822cbd76..e19667f16 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -39,9 +39,7 @@ class Project extends Model @emitter = new Emitter @buffers ?= [] - for buffer in @buffers - do (buffer) => - buffer.onDidDestroy => @removeBuffer(buffer) + @subscribeToBuffer(buffer) for buffer in @buffers Grim.deprecate("Pass 'paths' array instead of 'path' to project constructor") if path? paths ?= _.compact([path]) @@ -297,11 +295,11 @@ class Project extends Model addBuffer: (buffer, options={}) -> @addBufferAtIndex(buffer, @buffers.length, options) - buffer.onDidDestroy => @removeBuffer(buffer) + @subscribeToBuffer(buffer) addBufferAtIndex: (buffer, index, options={}) -> @buffers.splice(index, 0, buffer) - buffer.onDidDestroy => @removeBuffer(buffer) + @subscribeToBuffer(buffer) @emit 'buffer-created', buffer buffer @@ -330,6 +328,17 @@ class Project extends Model else @on 'buffer-created', (buffer) -> callback(buffer) + subscribeToBuffer: (buffer) -> + buffer.onDidDestroy => @removeBuffer(buffer) + buffer.onWillThrowWatchError ({error, handle}) => + handle() + atom.notifications.addWarning """ + Unable to read file after file change event. + Make sure you have permission to access the file. + """, + detail: error.message + dismissable: true + # Deprecated: delegate registerOpener: (opener) -> deprecate("Use Workspace::addOpener instead") From 1e7da343462b4079b4a64b4ce8be2ab7908c7316 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 8 Jan 2015 17:23:21 -0800 Subject: [PATCH 11/35] Use code rather than name for custom error --- src/project.coffee | 2 +- src/workspace.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index e19667f16..e0289c34a 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -283,7 +283,7 @@ class Project extends Model buildBuffer: (absoluteFilePath) -> if fs.getSizeSync(absoluteFilePath) >= 2 * 1048576 # 2MB error = new Error("Atom can only handle files < 2MB for now.") - error.name = 'OversizeFileError' + error.code = 'EFILETOOLARGE' throw error buffer = new TextBuffer({filePath: absoluteFilePath}) diff --git a/src/workspace.coffee b/src/workspace.coffee index 3c11c7d27..e86401d86 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -451,7 +451,7 @@ class Workspace extends Model try item ?= atom.project.open(uri, options) catch error - if error.name is 'OversizeFileError' + if error.code is 'EFILETOOLARGE' atom.notifications.addWarning(error.message + " Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") else if error.code is 'ENOENT' and error.path? atom.notifications.addWarning("No such file '#{error.path}'") From 4af007dce36c054b6a76198975b03128a21a7958 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 8 Jan 2015 17:37:35 -0800 Subject: [PATCH 12/35] :lipstick: --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index e86401d86..4d4263820 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -452,7 +452,7 @@ class Workspace extends Model item ?= atom.project.open(uri, options) catch error if error.code is 'EFILETOOLARGE' - atom.notifications.addWarning(error.message + " Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") + atom.notifications.addWarning("#{error.message} Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") else if error.code is 'ENOENT' and error.path? atom.notifications.addWarning("No such file '#{error.path}'") else if error.code is 'EACCES' and error.path? From f30992c5f285fd97bba8b7acfc19421d784675f5 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 9 Jan 2015 12:06:26 -0800 Subject: [PATCH 13/35] Upgrade pathwatcher and text-buffer for error catching --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d1cd885c..03dca53f7 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.7.0", - "text-buffer": "^3.8.4", + "text-buffer": "^3.9.0", "theorist": "^1.0.2", "underscore-plus": "^1.6.6", "vm-compatibility-layer": "0.1.0" From 4b8e98af0cb7833eb95b02839f45ff3735592edb Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 9 Jan 2015 16:25:00 -0800 Subject: [PATCH 14/35] Display a better message when there are config watch errors on startup --- src/config.coffee | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 721c522ad..bd2e9c5dc 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -832,25 +832,26 @@ class Config @configFileHasErrors = false catch error @configFileHasErrors = true - @notifyFailure('Failed to load config.cson', error) + @notifyFailure('Failed to load config.cson', error.message) observeUserConfig: -> try @watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) => @debouncedLoad() if eventType is 'change' and @watchSubscription? catch error - @notifyFailure('Failed to watch user config', error) + @notifyFailure """ + Unable to watch path: `config.cson`. Make sure you have permissions to + `~/.atom/config.cson`. On linux there are currently problems with watch + sizes. See [this document][watches] for more info. + [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path + """ unobserveUserConfig: -> @watchSubscription?.close() @watchSubscription = null - notifyFailure: (errorMessage, error) -> - message = "#{errorMessage}" - detail = error.stack - atom.notifications.addError(message, {detail, dismissable: true}) - console.error message - console.error detail + notifyFailure: (errorMessage, detail) -> + atom.notifications.addError(errorMessage, {detail, dismissable: true}) save: -> allSettings = {'*': @settings} From 85844f03f742f4455dd7b43e90ef4a84a9ca0fcc Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 9 Jan 2015 17:12:56 -0800 Subject: [PATCH 15/35] Display better error on unable to watch keycap.cson --- src/keymap-extensions.coffee | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index a6e6bcddc..ef26aa226 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -20,12 +20,27 @@ KeymapManager::getUserKeymapPath = -> KeymapManager::loadUserKeymap = -> userKeymapPath = @getUserKeymapPath() - if fs.isFileSync(userKeymapPath) + return unless fs.isFileSync(userKeymapPath) + + try @loadKeymap(userKeymapPath, watch: true, suppressErrors: true) + catch error + if error.message.indexOf('Unable to watch path') > -1 + message = """ + Unable to watch path: `keymap.cson`. Make sure you have permissions to + `~/.atom/keymap.cson`. On linux there are currently problems with watch + sizes. See [this document][watches] for more info. + [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path + """ + atom.notifications.addError(message, {detail, dismissable: true}) + else + detail = error.path + stack = error.stack + atom.notifications.addFatalError(error.message, {detail, stack, dismissable: true}) KeymapManager::subscribeToFileReadFailure = -> this.onDidFailToReadFile (error) -> - atom.notifications.addError('Failed to load keymap.cson', {detail: error.stack, dismissable: true}) + atom.notifications.addError('Failed to load keymap.cson', {detail: error.message, dismissable: true}) # This enables command handlers registered via jQuery to call # `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler. From 9bb6a18d411ee612a79465b88b72e71365aa492e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 9 Jan 2015 17:40:09 -0800 Subject: [PATCH 16/35] Support empty paths --- src/project.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index e0289c34a..7274c94f2 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -217,9 +217,10 @@ class Project extends Model open: (filePath, options={}) -> filePath = @resolvePath(filePath) - # Make sure we have permissions - fileDescriptor = fs.openSync(filePath, 'r+') - fs.closeSync(fileDescriptor) + if filePath? + # Make sure we have permissions + fileDescriptor = fs.openSync(filePath, 'r+') + fs.closeSync(fileDescriptor) @bufferForPath(filePath).then (buffer) => @buildEditorForBuffer(buffer, options) From 2306f16b304b50e7fea4503d5e2746b44c3d4dfc Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 13:27:37 -0800 Subject: [PATCH 17/35] :up_arrow: snippets for better errors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03dca53f7..7b89e4b9d 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "package-generator": "0.36.0", "release-notes": "0.45.0", "settings-view": "0.172.0", - "snippets": "0.67.0", + "snippets": "0.68.0", "spell-check": "0.50.0", "status-bar": "0.57.0", "styleguide": "0.42.0", From 02e4482def462cdebda21b2f498a1929dbcd9fab Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 13:40:44 -0800 Subject: [PATCH 18/35] :up_arrow: command-palette@0.33.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b89e4b9d..e09a59065 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "background-tips": "0.20.0", "bookmarks": "0.33.0", "bracket-matcher": "0.67.0", - "command-palette": "0.32.0", + "command-palette": "0.33.0", "deprecation-cop": "0.29.0", "dev-live-reload": "0.36.0", "encoding-selector": "0.14.0", From cbd42ac20c8770e28dbd0a6e373e3d4f37d69b5a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 14:19:02 -0800 Subject: [PATCH 19/35] =?UTF-8?q?Don=E2=80=99t=20need=20the=20pane=20in=20?= =?UTF-8?q?workspace.open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/workspace-spec.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 6c84c8f9b..382a2eb30 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -278,7 +278,7 @@ describe "Workspace", -> it "creates a notification", -> waitsForPromise -> - workspace.open('file1', workspace.getActivePane()) + workspace.open('file1') runs -> expect(notificationSpy).toHaveBeenCalled() @@ -289,7 +289,7 @@ describe "Workspace", -> describe "when a file does not exist", -> it "creates a notification", -> waitsForPromise -> - workspace.open('not-a-file.md', workspace.getActivePane()) + workspace.open('not-a-file.md') runs -> expect(notificationSpy).toHaveBeenCalled() @@ -308,7 +308,7 @@ describe "Workspace", -> it "creates a notification", -> waitsForPromise -> - workspace.open('file1', workspace.getActivePane()) + workspace.open('file1') runs -> expect(notificationSpy).toHaveBeenCalled() From 3454249b58b7abda5ea08e2d9b114a5edc44b769 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 14:19:27 -0800 Subject: [PATCH 20/35] Allow ENOENT errors in project.open --- spec/workspace-spec.coffee | 10 ++++------ src/project.coffee | 9 ++++++--- src/workspace.coffee | 15 +++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 382a2eb30..810278c7a 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -287,16 +287,14 @@ describe "Workspace", -> expect(notification.getMessage()).toContain '< 2MB' describe "when a file does not exist", -> - it "creates a notification", -> + it "creates an empty buffer for the specified path", -> waitsForPromise -> workspace.open('not-a-file.md') runs -> - expect(notificationSpy).toHaveBeenCalled() - notification = notificationSpy.mostRecentCall.args[0] - expect(notification.getType()).toBe 'warning' - expect(notification.getMessage()).toContain 'No such file' - expect(notification.getMessage()).toContain 'not-a-file.md' + editor = workspace.getActiveTextEditor() + expect(notificationSpy).not.toHaveBeenCalled() + expect(editor.getPath()).toContain 'not-a-file.md' describe "when the user does not have access to the file", -> beforeEach -> diff --git a/src/project.coffee b/src/project.coffee index 7274c94f2..0c2496ca6 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -218,9 +218,12 @@ class Project extends Model filePath = @resolvePath(filePath) if filePath? - # Make sure we have permissions - fileDescriptor = fs.openSync(filePath, 'r+') - fs.closeSync(fileDescriptor) + try + fileDescriptor = fs.openSync(filePath, 'r+') + fs.closeSync(fileDescriptor) + catch error + # allow ENOENT errors to create an editor for paths that dont exist + throw error unless error.code is 'ENOENT' @bufferForPath(filePath).then (buffer) => @buildEditorForBuffer(buffer, options) diff --git a/src/workspace.coffee b/src/workspace.coffee index 4d4263820..60b22f303 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -451,14 +451,13 @@ class Workspace extends Model try item ?= atom.project.open(uri, options) catch error - if error.code is 'EFILETOOLARGE' - atom.notifications.addWarning("#{error.message} Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") - else if error.code is 'ENOENT' and error.path? - atom.notifications.addWarning("No such file '#{error.path}'") - else if error.code is 'EACCES' and error.path? - atom.notifications.addWarning("Permission denied '#{error.path}'") - else - throw error + switch error.code + when 'EFILETOOLARGE' + atom.notifications.addWarning("#{error.message} Large file support is being tracked at [atom/atom#307](https://github.com/atom/atom/issues/307).") + when 'EACCES' + atom.notifications.addWarning("Permission denied '#{error.path}'") + else + throw error return Q() Q(item) From 4be793f465c0fe8902632a19aa1e5b5399c33734 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 15:12:02 -0800 Subject: [PATCH 21/35] =?UTF-8?q?Post=20notifications=20when=20the=20user?= =?UTF-8?q?=E2=80=99s=20sheet=20cannot=20be=20loaded=20/=20parsed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/theme-manager-spec.coffee | 122 ++++++++++++++++++++------------- src/theme-manager.coffee | 33 ++++++--- 2 files changed, 99 insertions(+), 56 deletions(-) diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index a9bacfe90..dc4cf7e0f 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -289,69 +289,95 @@ describe "ThemeManager", -> # from within the theme itself expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)" + describe "user stylesheet", -> + describe "when the user stylesheet changes", -> + beforeEach -> + jasmine.snapshotDeprecations() - describe "when the user stylesheet changes", -> - beforeEach -> - jasmine.snapshotDeprecations() + afterEach -> + jasmine.restoreDeprecationsSnapshot() - afterEach -> - jasmine.restoreDeprecationsSnapshot() + it "reloads it", -> + [styleElementAddedHandler, styleElementRemovedHandler] = [] + [stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = [] + userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less') + fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}') + spyOn(atom.styles, 'getUserStyleSheetPath').andReturn userStylesheetPath - it "reloads it", -> - [styleElementAddedHandler, styleElementRemovedHandler] = [] - [stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = [] - userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less') - fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}') - spyOn(atom.styles, 'getUserStyleSheetPath').andReturn userStylesheetPath + waitsForPromise -> + themeManager.activateThemes() - waitsForPromise -> - themeManager.activateThemes() + runs -> + atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") + atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") - runs -> - atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") - atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") + themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") + themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") + themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") + spyOn(themeManager, 'loadUserStylesheet').andCallThrough() - themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") - themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") - themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") - spyOn(themeManager, 'loadUserStylesheet').andCallThrough() + expect($(document.body).css('border-style')).toBe 'dotted' + fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}') - expect($(document.body).css('border-style')).toBe 'dotted' - fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}') + waitsFor -> + themeManager.loadUserStylesheet.callCount is 1 - waitsFor -> - themeManager.loadUserStylesheet.callCount is 1 + runs -> + expect($(document.body).css('border-style')).toBe 'dashed' - runs -> - expect($(document.body).css('border-style')).toBe 'dashed' + expect(styleElementRemovedHandler).toHaveBeenCalled() + expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted' + expect(stylesheetRemovedHandler).toHaveBeenCalled() + expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted' - expect(styleElementRemovedHandler).toHaveBeenCalled() - expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted' - expect(stylesheetRemovedHandler).toHaveBeenCalled() - expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted' + expect(styleElementAddedHandler).toHaveBeenCalled() + expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed' + expect(stylesheetAddedHandler).toHaveBeenCalled() + expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' - expect(styleElementAddedHandler).toHaveBeenCalled() - expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed' - expect(stylesheetAddedHandler).toHaveBeenCalled() - expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' + expect(stylesheetsChangedHandler).toHaveBeenCalled() - expect(stylesheetsChangedHandler).toHaveBeenCalled() + styleElementRemovedHandler.reset() + stylesheetRemovedHandler.reset() + stylesheetsChangedHandler.reset() + fs.removeSync(userStylesheetPath) - styleElementRemovedHandler.reset() - stylesheetRemovedHandler.reset() - stylesheetsChangedHandler.reset() - fs.removeSync(userStylesheetPath) + waitsFor -> + themeManager.loadUserStylesheet.callCount is 2 - waitsFor -> - themeManager.loadUserStylesheet.callCount is 2 + runs -> + expect(styleElementRemovedHandler).toHaveBeenCalled() + expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed' + expect(stylesheetRemovedHandler).toHaveBeenCalled() + expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' + expect($(document.body).css('border-style')).toBe 'none' + expect(stylesheetsChangedHandler).toHaveBeenCalled() - runs -> - expect(styleElementRemovedHandler).toHaveBeenCalled() - expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed' - expect(stylesheetRemovedHandler).toHaveBeenCalled() - expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' - expect($(document.body).css('border-style')).toBe 'none' - expect(stylesheetsChangedHandler).toHaveBeenCalled() + describe "when there is an error reading the stylesheet", -> + addErrorHandler = null + beforeEach -> + atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() + + it "creates an error notification", -> + themeManager.loadUserStylesheet() + expect(addErrorHandler).toHaveBeenCalled() + note = addErrorHandler.mostRecentCall.args[0] + expect(note.getType()).toBe 'error' + expect(note.getMessage()).toContain 'Error loading' + + describe "when there is an error watching the user stylesheet", -> + addErrorHandler = null + beforeEach -> + # styles.less already cannot be watched, so we just need to suppress the load error. + spyOn(themeManager, 'loadStylesheet').andReturn '' + atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() + + it "creates an error notification", -> + themeManager.loadUserStylesheet() + expect(addErrorHandler).toHaveBeenCalled() + note = addErrorHandler.mostRecentCall.args[0] + expect(note.getType()).toBe 'error' + expect(note.getMessage()).toContain 'Unable to watch path' describe "when a non-existent theme is present in the config", -> beforeEach -> diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 23ef0ef69..24a86f05a 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -252,10 +252,21 @@ class ThemeManager userStylesheetPath = atom.styles.getUserStyleSheetPath() return unless fs.isFileSync(userStylesheetPath) - @userStylesheetFile = new File(userStylesheetPath) - @userStylesheetFile.on 'contents-changed moved removed', => @loadUserStylesheet() - userStylesheetContents = @loadStylesheet(userStylesheetPath, true) + try + @userStylesheetFile = new File(userStylesheetPath) + @userStylesheetFile.on 'contents-changed moved removed', => @loadUserStylesheet() + catch error + message = """ + Unable to watch path: `#{path.basename(userStylesheetPath)}`. Make sure + you have permissions to `#{userStylesheetPath}`. + On linux there are currently problems with watch sizes. See + [this document][watches] for more info. + [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path + """ + atom.notifications.addError(message, dismissable: true) + + userStylesheetContents = @loadStylesheet(userStylesheetPath, true) @userStyleSheetDisposable = atom.styles.addStyleSheet(userStylesheetContents, sourcePath: userStylesheetPath, priority: 2) loadBaseStylesheets: -> @@ -298,11 +309,17 @@ class ThemeManager else @lessCache.read(lessStylesheetPath) catch error - console.error """ - Error compiling Less stylesheet: #{lessStylesheetPath} - Line number: #{error.line} - #{error.message} - """ + if error.line? + message = "Error compiling Less stylesheet: `#{lessStylesheetPath}`" + detail = """ + Line number: #{error.line} + #{error.message} + """ + else + message = "Error loading Less stylesheet: `#{lessStylesheetPath}`" + detail = error.message + + atom.notifications.addError(message, {detail, dismissable: true}) removeStylesheet: (stylesheetPath) -> @styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose() From 1bc2248fc2862c6d3131ed86415ed6e3ec02014e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 15:38:47 -0800 Subject: [PATCH 22/35] Fix config specs --- spec/config-spec.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index c2a6f304f..26cb97af7 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -686,15 +686,14 @@ describe "Config", -> expect(observeHandler).toHaveBeenCalledWith 'baz' describe "when the config file contains invalid cson", -> + addErrorHandler = null beforeEach -> - spyOn(console, 'error') - spyOn(atom.notifications, 'addError') + atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() fs.writeFileSync(atom.config.configFilePath, "{{{{{") it "logs an error to the console and does not overwrite the config file on a subsequent save", -> atom.config.loadUserConfig() - expect(console.error).toHaveBeenCalled() - expect(atom.notifications.addError.callCount).toBe 1 + expect(addErrorHandler.callCount).toBe 1 atom.config.set("hair", "blonde") # trigger a save expect(atom.config.save).not.toHaveBeenCalled() @@ -876,10 +875,11 @@ describe "Config", -> expect(atom.config.get('foo.bar')).toBe 'newVal' describe "when the config file changes to contain invalid cson", -> + addErrorHandler = null beforeEach -> - spyOn(console, 'error') + atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() writeConfigFile("}}}") - waitsFor "error to be logged", -> console.error.callCount > 0 + waitsFor "error to be logged", -> addErrorHandler.callCount > 0 it "logs a warning and does not update config data", -> expect(updatedHandler.callCount).toBe 0 From 3ec3c2b69da8ff008f29d1c9834490e47af2def3 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 15:47:43 -0800 Subject: [PATCH 23/35] Fix theme manager specs --- spec/theme-manager-spec.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index dc4cf7e0f..96ea55468 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -356,6 +356,9 @@ describe "ThemeManager", -> describe "when there is an error reading the stylesheet", -> addErrorHandler = null beforeEach -> + themeManager.loadUserStylesheet() + spyOn(themeManager.lessCache, 'cssForFile').andCallFake -> + throw new Error('EACCES permission denied "styles.less"') atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() it "creates an error notification", -> @@ -368,7 +371,10 @@ describe "ThemeManager", -> describe "when there is an error watching the user stylesheet", -> addErrorHandler = null beforeEach -> - # styles.less already cannot be watched, so we just need to suppress the load error. + {File} = require 'pathwatcher' + spyOn(File::, 'on').andCallFake (event) -> + if event.indexOf('contents-changed') > -1 + throw new Error('Unable to watch path') spyOn(themeManager, 'loadStylesheet').andReturn '' atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() From 683203a9a10889c6b299876a3ac87abaf602b2eb Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 16:00:00 -0800 Subject: [PATCH 24/35] Attempt to fix theme manager specs They work for me! --- spec/theme-manager-spec.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index 96ea55468..ffbabc7cf 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -290,6 +290,12 @@ describe "ThemeManager", -> expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)" describe "user stylesheet", -> + userStylesheetPath = null + beforeEach -> + userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less') + fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}') + spyOn(atom.styles, 'getUserStyleSheetPath').andReturn userStylesheetPath + describe "when the user stylesheet changes", -> beforeEach -> jasmine.snapshotDeprecations() @@ -300,9 +306,6 @@ describe "ThemeManager", -> it "reloads it", -> [styleElementAddedHandler, styleElementRemovedHandler] = [] [stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = [] - userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less') - fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}') - spyOn(atom.styles, 'getUserStyleSheetPath').andReturn userStylesheetPath waitsForPromise -> themeManager.activateThemes() From ba40706265ab8c1d60df1ba4ff106fff398ea747 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 13 Jan 2015 17:02:49 -0800 Subject: [PATCH 25/35] =?UTF-8?q?Add=20a=20notification=20when=20the=20ini?= =?UTF-8?q?t=20script=20can=E2=80=99t=20be=20loaded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/atom.coffee b/src/atom.coffee index c515f26be..0e2031e62 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -776,7 +776,9 @@ class Atom extends Model try require(userInitScriptPath) if fs.isFileSync(userInitScriptPath) catch error - console.error "Failed to load `#{userInitScriptPath}`", error.stack, error + atom.notifications.addError "Failed to load `#{userInitScriptPath}`", + detail: error.message + dismissable: true # Require the module with the given globals. # From 62eac3f8a5754275fea654ac48b52e9351ca9372 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 09:23:45 -0800 Subject: [PATCH 26/35] Fix error string when there is a user config error --- src/config.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config.coffee b/src/config.coffee index bd2e9c5dc..415c6d2e2 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -832,7 +832,10 @@ class Config @configFileHasErrors = false catch error @configFileHasErrors = true - @notifyFailure('Failed to load config.cson', error.message) + if error.location? + @notifyFailure('Failed to load config.cson', error.stack) + else + @notifyFailure('Failed to load config.cson', error.message) observeUserConfig: -> try From 6211f7330f27355df806e83d2aefce59abc81394 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 11:14:56 -0800 Subject: [PATCH 27/35] Use path name in config error messsages --- src/config.coffee | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 415c6d2e2..6aa67c008 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -832,10 +832,17 @@ class Config @configFileHasErrors = false catch error @configFileHasErrors = true - if error.location? - @notifyFailure('Failed to load config.cson', error.stack) + fileName = path.basename(@configFilePath) + message = "Failed to load `#{fileName}`" + + detail = if error.location? + # stack is the output from CSON in this case + error.stack else - @notifyFailure('Failed to load config.cson', error.message) + # message will be EACCES permission denied, et al + error.message + + @notifyFailure(message, detail) observeUserConfig: -> try @@ -843,8 +850,8 @@ class Config @debouncedLoad() if eventType is 'change' and @watchSubscription? catch error @notifyFailure """ - Unable to watch path: `config.cson`. Make sure you have permissions to - `~/.atom/config.cson`. On linux there are currently problems with watch + Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to + `#{@configFilePath}`. On linux there are currently problems with watch sizes. See [this document][watches] for more info. [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path """ From e51e8596310f101f189887ea195b56a815be55d6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 12:17:01 -0800 Subject: [PATCH 28/35] Clean up error messages for keymap.cson Use the computed path as well --- src/keymap-extensions.coffee | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index ef26aa226..55de04785 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -27,20 +27,30 @@ KeymapManager::loadUserKeymap = -> catch error if error.message.indexOf('Unable to watch path') > -1 message = """ - Unable to watch path: `keymap.cson`. Make sure you have permissions to - `~/.atom/keymap.cson`. On linux there are currently problems with watch - sizes. See [this document][watches] for more info. + Unable to watch path: `#{path.basename(userKeymapPath)}`. Make sure you + have permission to read `#{userKeymapPath}`. + + On linux there are currently problems with watch sizes. See + [this document][watches] for more info. [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path """ - atom.notifications.addError(message, {detail, dismissable: true}) + atom.notifications.addError(message, {dismissable: true}) else detail = error.path stack = error.stack atom.notifications.addFatalError(error.message, {detail, stack, dismissable: true}) KeymapManager::subscribeToFileReadFailure = -> - this.onDidFailToReadFile (error) -> - atom.notifications.addError('Failed to load keymap.cson', {detail: error.message, dismissable: true}) + this.onDidFailToReadFile (error) => + userKeymapPath = @getUserKeymapPath() + message = "Failed to load `#{userKeymapPath}`" + + detail = if error.location? + error.stack + else + error.message + + atom.notifications.addError(message, {detail: detail, dismissable: true}) # This enables command handlers registered via jQuery to call # `.abortKeyBinding()` on the `jQuery.Event` object passed to the handler. From 2355862101fbe55f360f70fb08e010c979ee85fb Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 12:42:00 -0800 Subject: [PATCH 29/35] :arrow_up: pathwatcher@2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e09a59065..47638aefd 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "nslog": "^1.0.1", "oniguruma": "^3.0.4", "optimist": "0.4.0", - "pathwatcher": "^2.5.0", + "pathwatcher": "^2.6.0", "property-accessors": "^1", "q": "^1.0.1", "random-words": "0.0.1", From 3dc908c5ffa66d03cf59ed9265c404ff4b110284 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 12:46:31 -0800 Subject: [PATCH 30/35] Use `eventType` from pathwatcher's watch errors in messages --- spec/project-spec.coffee | 5 ++++- src/project.coffee | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 930b4a849..5ff1b2df3 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -60,15 +60,18 @@ describe "Project", -> it "creates a warning notification", -> atom.notifications.onDidAddNotification noteSpy = jasmine.createSpy() + error = new Error('SomeError') + error.eventType = 'resurrect' editor.buffer.emitter.emit 'will-throw-watch-error', handle: jasmine.createSpy() - error: new Error('SomeError') + error: error expect(noteSpy).toHaveBeenCalled() notification = noteSpy.mostRecentCall.args[0] expect(notification.getType()).toBe 'warning' expect(notification.getDetail()).toBe 'SomeError' + expect(notification.getMessage()).toContain '`resurrect`' describe ".open(path)", -> [absolutePath, newBufferHandler] = [] diff --git a/src/project.coffee b/src/project.coffee index 0c2496ca6..c6d2ea34c 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -337,8 +337,8 @@ class Project extends Model buffer.onWillThrowWatchError ({error, handle}) => handle() atom.notifications.addWarning """ - Unable to read file after file change event. - Make sure you have permission to access the file. + Unable to read file after file `#{error.eventType}` event. + Make sure you have permission to access `#{@getPath()}`. """, detail: error.message dismissable: true From 5fe44761146dc853b52fbf5501da87caa7b9fb04 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 12:51:12 -0800 Subject: [PATCH 31/35] :arrow_up: text-buffer@3.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 47638aefd..038a86923 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.7.0", - "text-buffer": "^3.9.0", + "text-buffer": "^3.10.0", "theorist": "^1.0.2", "underscore-plus": "^1.6.6", "vm-compatibility-layer": "0.1.0" From 4ba7182bbf4c1866fe5c304d26836d52f27cec86 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 12:57:52 -0800 Subject: [PATCH 32/35] :lipstick: Inline fileName cause it's used once --- src/config.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 6aa67c008..41329349b 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -832,8 +832,7 @@ class Config @configFileHasErrors = false catch error @configFileHasErrors = true - fileName = path.basename(@configFilePath) - message = "Failed to load `#{fileName}`" + message = "Failed to load `#{path.basename(@configFilePath)}`" detail = if error.location? # stack is the output from CSON in this case From 7d3fe78eed2cff486cfaba56d6ce10bc972a9364 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 15:17:16 -0800 Subject: [PATCH 33/35] Remove Pane require from workspace view specs --- spec/workspace-view-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 7368efe79..586e72fe8 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -3,7 +3,6 @@ Q = require 'q' path = require 'path' temp = require 'temp' TextEditorView = require '../src/text-editor-view' -Pane = require '../src/pane' PaneView = require '../src/pane-view' Workspace = require '../src/workspace' From 274ae6cd578b3d0fd6153a55fc43107bad7b2599 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 15:17:55 -0800 Subject: [PATCH 34/35] Use buffer.getPath not @getPath in project :grimacing: --- spec/project-spec.coffee | 1 + src/project.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 5ff1b2df3..32022ca98 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -72,6 +72,7 @@ describe "Project", -> expect(notification.getType()).toBe 'warning' expect(notification.getDetail()).toBe 'SomeError' expect(notification.getMessage()).toContain '`resurrect`' + expect(notification.getMessage()).toContain 'fixtures/dir/a' describe ".open(path)", -> [absolutePath, newBufferHandler] = [] diff --git a/src/project.coffee b/src/project.coffee index c6d2ea34c..0ae0f7cf7 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -338,7 +338,7 @@ class Project extends Model handle() atom.notifications.addWarning """ Unable to read file after file `#{error.eventType}` event. - Make sure you have permission to access `#{@getPath()}`. + Make sure you have permission to access `#{buffer.getPath()}`. """, detail: error.message dismissable: true From 5cb18fc7e5a1cbd9bee5d0b2802146d4b85f14c6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 14 Jan 2015 15:18:03 -0800 Subject: [PATCH 35/35] this. -> @ --- src/keymap-extensions.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index 55de04785..821abbc0c 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -41,7 +41,7 @@ KeymapManager::loadUserKeymap = -> atom.notifications.addFatalError(error.message, {detail, stack, dismissable: true}) KeymapManager::subscribeToFileReadFailure = -> - this.onDidFailToReadFile (error) => + @onDidFailToReadFile (error) => userKeymapPath = @getUserKeymapPath() message = "Failed to load `#{userKeymapPath}`"