diff --git a/package.json b/package.json index 0d1cd885c..038a86923 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", @@ -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.10.0", "theorist": "^1.0.2", "underscore-plus": "^1.6.6", "vm-compatibility-layer": "0.1.0" @@ -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", @@ -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", 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 diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index ba91fbde7..32022ca98 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -51,6 +51,29 @@ 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() + + error = new Error('SomeError') + error.eventType = 'resurrect' + editor.buffer.emitter.emit 'will-throw-watch-error', + handle: jasmine.createSpy() + 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`' + expect(notification.getMessage()).toContain 'fixtures/dir/a' + describe ".open(path)", -> [absolutePath, newBufferHandler] = [] diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index a9bacfe90..ffbabc7cf 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -289,69 +289,104 @@ describe "ThemeManager", -> # from within the theme itself expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)" - - describe "when the user stylesheet changes", -> + describe "user stylesheet", -> + userStylesheetPath = null beforeEach -> - jasmine.snapshotDeprecations() - - 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 - waitsForPromise -> - themeManager.activateThemes() + describe "when the user stylesheet changes", -> + beforeEach -> + jasmine.snapshotDeprecations() - runs -> - atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") - atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") + afterEach -> + jasmine.restoreDeprecationsSnapshot() - themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") - themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") - themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") - spyOn(themeManager, 'loadUserStylesheet').andCallThrough() + it "reloads it", -> + [styleElementAddedHandler, styleElementRemovedHandler] = [] + [stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = [] - expect($(document.body).css('border-style')).toBe 'dotted' - fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}') + waitsForPromise -> + themeManager.activateThemes() - waitsFor -> - themeManager.loadUserStylesheet.callCount is 1 + runs -> + atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") + atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") - runs -> - expect($(document.body).css('border-style')).toBe 'dashed' + themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") + themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") + themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") + spyOn(themeManager, 'loadUserStylesheet').andCallThrough() - 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($(document.body).css('border-style')).toBe 'dotted' + fs.writeFileSync(userStylesheetPath, 'body {border-style: 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' + waitsFor -> + themeManager.loadUserStylesheet.callCount is 1 - expect(stylesheetsChangedHandler).toHaveBeenCalled() + runs -> + expect($(document.body).css('border-style')).toBe 'dashed' - styleElementRemovedHandler.reset() - stylesheetRemovedHandler.reset() - stylesheetsChangedHandler.reset() - fs.removeSync(userStylesheetPath) + 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' - waitsFor -> - themeManager.loadUserStylesheet.callCount is 2 + 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' - 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() + expect(stylesheetsChangedHandler).toHaveBeenCalled() + + styleElementRemovedHandler.reset() + stylesheetRemovedHandler.reset() + stylesheetsChangedHandler.reset() + fs.removeSync(userStylesheetPath) + + 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() + + 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", -> + 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 -> + {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() + + 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/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 16bcdbcab..810278c7a 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' @@ -266,6 +267,63 @@ 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') + + runs -> + expect(notificationSpy).toHaveBeenCalled() + notification = notificationSpy.mostRecentCall.args[0] + expect(notification.getType()).toBe 'warning' + expect(notification.getMessage()).toContain '< 2MB' + + describe "when a file does not exist", -> + it "creates an empty buffer for the specified path", -> + waitsForPromise -> + workspace.open('not-a-file.md') + + runs -> + 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 -> + 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') + + 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 "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", -> pane = workspace.getActivePane() @@ -857,3 +915,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 ade0dcc4d..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' @@ -295,39 +294,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 -> - throw new Error("EACCES, permission denied '/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 file cannot be saved", -> - spyOn(Pane::, 'saveActiveItem').andCallFake -> - throw new Error("no one knows") - - save = -> atom.workspace.saveActivePaneItem() - expect(save).toThrow() 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. # diff --git a/src/config.coffee b/src/config.coffee index 721c522ad..41329349b 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -832,25 +832,35 @@ class Config @configFileHasErrors = false catch error @configFileHasErrors = true - @notifyFailure('Failed to load config.cson', error) + message = "Failed to load `#{path.basename(@configFilePath)}`" + + detail = if error.location? + # stack is the output from CSON in this case + error.stack + else + # message will be EACCES permission denied, et al + error.message + + @notifyFailure(message, detail) 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: `#{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 + """ 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} diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index a6e6bcddc..821abbc0c 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -20,12 +20,37 @@ 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: `#{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, {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}) + @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. diff --git a/src/project.coffee b/src/project.coffee index a96788a53..0ae0f7cf7 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]) @@ -218,6 +216,15 @@ class Project extends Model # Returns a promise that resolves to an {TextEditor}. open: (filePath, options={}) -> filePath = @resolvePath(filePath) + + if filePath? + 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) @@ -279,7 +286,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.code = 'EFILETOOLARGE' + throw error buffer = new TextBuffer({filePath: absoluteFilePath}) buffer.setEncoding(atom.config.get('core.fileEncoding')) @@ -290,11 +299,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 @@ -323,6 +332,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 `#{error.eventType}` event. + Make sure you have permission to access `#{buffer.getPath()}`. + """, + detail: error.message + dismissable: true + # Deprecated: delegate registerOpener: (opener) -> deprecate("Use Workspace::addOpener instead") 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() diff --git a/src/workspace.coffee b/src/workspace.coffee index a6637ffc3..60b22f303 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -447,7 +447,18 @@ 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 + 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) .then (item) => @@ -612,8 +623,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")