From 538500042c7d3878aa0afb7060343ff1e05bf400 Mon Sep 17 00:00:00 2001 From: Collin Donahue-Oponski Date: Mon, 16 Nov 2015 22:05:23 -0700 Subject: [PATCH 001/198] :bug: Autoscroll to cursor after clearing multi-cursor selection. --- src/text-editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e5b3bd481..501c52a43 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2445,6 +2445,7 @@ class TextEditor extends Model selections = @getSelections() if selections.length > 1 selection.destroy() for selection in selections[1...(selections.length)] + selections[0].autoscroll() true else false From e4e200317abcd6c484ba834e4bac32ad27463452 Mon Sep 17 00:00:00 2001 From: Collin Donahue-Oponski Date: Fri, 11 Dec 2015 15:32:29 -0700 Subject: [PATCH 002/198] :white_check_mark: Autoscroll to cursor after clearing multi-cursor selection. --- spec/text-editor-spec.coffee | 32 +++++++++++++++++++++++++++----- src/text-editor.coffee | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 02d2e4a96..a99e95ee6 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2119,20 +2119,42 @@ describe "TextEditor", -> editor.splitSelectionsIntoLines() expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]] - describe ".consolidateSelections()", -> - it "destroys all selections but the least recent, returning true if any selections were destroyed", -> - editor.setSelectedBufferRange([[3, 16], [3, 21]]) - selection1 = editor.getLastSelection() + describe "::consolidateSelections()", -> + makeMultipleSelections = -> + selection.setBufferRange [[3, 16], [3, 21]] selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]]) selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]]) + selection4 = editor.addSelectionForBufferRange([[1, 6], [1, 10]]) + expect(editor.getSelections()).toEqual [selection, selection2, selection3, selection4] + [selection, selection2, selection3, selection4] + + it "destroys all selections but the least recent, returning true if any selections were destroyed", -> + [selection1] = makeMultipleSelections() - expect(editor.getSelections()).toEqual [selection1, selection2, selection3] expect(editor.consolidateSelections()).toBeTruthy() expect(editor.getSelections()).toEqual [selection1] expect(selection1.isEmpty()).toBeFalsy() expect(editor.consolidateSelections()).toBeFalsy() expect(editor.getSelections()).toEqual [selection1] + it "scrolls to the remaining selection", -> + [selection1] = makeMultipleSelections() + + atom.config.set('editor.scrollPastEnd', true) + editor.setHeight(100, true) + editor.setLineHeightInPixels(10) + editor.setFirstVisibleScreenRow(10) + expect(editor.getVisibleRowRange()[0]).toBeGreaterThan(selection1.getBufferRowRange()[1]) + + editor.consolidateSelections() + + waitsForPromise -> + new Promise((resolve) -> window.requestAnimationFrame(resolve)) + + runs -> + expect(editor.getVisibleRowRange()[0]).not.toBeGreaterThan(selection1.getBufferRowRange()[0]) + expect(editor.getVisibleRowRange()[1]).not.toBeLessThan(selection1.getBufferRowRange()[0]) + describe "when the cursor is moved while there is a selection", -> makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]] diff --git a/src/text-editor.coffee b/src/text-editor.coffee index ba54b9cd4..d4d0737e2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2437,7 +2437,7 @@ class TextEditor extends Model selections = @getSelections() if selections.length > 1 selection.destroy() for selection in selections[1...(selections.length)] - selections[0].autoscroll() + selections[0].autoscroll(center: true) true else false From 43e34d4f51a899071fb29af6cc14400113cef7ae Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Mon, 4 Jan 2016 05:59:11 -0700 Subject: [PATCH 003/198] :checkered_flag: Use PowerShell to get User path in Windows when installing via Squirrel, replacing reg.exe that fails to execute if registry editing is disabled in the group policy. This was causing the user Path to get deleted if registry editing is disabled. The workaround that was in place kept the existing path, but failed to add Atom to it. --- src/browser/squirrel-update.coffee | 50 +++++++++++++----------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 0e4743a21..4efc089d7 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -11,9 +11,11 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') regPath = path.join(system32Path, 'reg.exe') + powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else regPath = 'reg.exe' + powershellPath = 'powershell.exe' setxPath = 'setx.exe' # Registry keys used for context menu @@ -43,11 +45,23 @@ spawn = (command, args, callback) -> error?.code ?= code error?.stdout ?= stdout callback?(error, stdout) + # This is necessary if using Powershell 2 on Windows 7 to get the events to raise + # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs + spawnedProcess.stdin.end() + # Spawn reg.exe and callback when it completes spawnReg = (args, callback) -> spawn(regPath, args, callback) +# Spawn powershell.exe and callback when it completes +spawnPowershell = (args, callback) -> + args.unshift('-command') + args.unshift('RemoteSigned') + args.unshift('-ExecutionPolicy') + args.unshift('-noprofile') + spawn(powershellPath, args, callback) + # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> spawn(setxPath, args, callback) @@ -85,37 +99,17 @@ isAscii = (text) -> # Get the user's PATH environment variable registry value. getPath = (callback) -> - spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) -> + spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> if error? - if error.code is 1 - # FIXME Don't overwrite path when reading value is disabled - # https://github.com/atom/atom/issues/5092 - if stdout.indexOf('ERROR: Registry editing has been disabled by your administrator.') isnt -1 - return callback(error) + return callback(error) - # The query failed so the Path does not exist yet in the registry - return callback(null, '') - else - return callback(error) - - # Registry query output is in the form: - # - # HKEY_CURRENT_USER\Environment - # Path REG_SZ C:\a\folder\on\the\path;C\another\folder - # - - lines = stdout.split(/[\r\n]+/).filter (line) -> line - segments = lines[lines.length - 1]?.split(' ') - if segments[1] is 'Path' and segments.length >= 3 - pathEnv = segments?[3..].join(' ') - if isAscii(pathEnv) - callback(null, pathEnv) - else - # FIXME Don't corrupt non-ASCII PATH values - # https://github.com/atom/atom/issues/5063 - callback(new Error('PATH contains non-ASCII values')) + pathOutput = stdout.replace(/^\s+|\s+$/g, '') + if isAscii(pathOutput) + callback(null, pathOutput) else - callback(new Error('Registry query for PATH failed')) + # FIXME Don't corrupt non-ASCII PATH values + # https://github.com/atom/atom/issues/5063 + callback(new Error('PATH contains non-ASCII values')) # Uninstall the Open with Atom explorer context menu items via the registry. uninstallContextMenu = (callback) -> From 91253910ef5b966e40f57b74366da5a0cec48144 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Wed, 6 Jan 2016 12:38:55 -0700 Subject: [PATCH 004/198] :checkered_flag: Fix corrupting Windows Paths containing non-Ascii characters Remove workaround that just didn't update the path at all - now including the Atom path update and preserving the non-Ascii characters --- src/browser/squirrel-update.coffee | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 4efc089d7..f9df3c0b5 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -56,6 +56,9 @@ spawnReg = (args, callback) -> # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> + # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding + # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell + args[0] = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\r\n$output=#{args[0]}\r\n[Console]::WriteLine($output)" args.unshift('-command') args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') @@ -90,13 +93,6 @@ installContextMenu = (callback) -> installMenu directoryKeyPath, '%1', -> installMenu(backgroundKeyPath, '%V', callback) -isAscii = (text) -> - index = 0 - while index < text.length - return false if text.charCodeAt(index) > 127 - index++ - true - # Get the user's PATH environment variable registry value. getPath = (callback) -> spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> @@ -104,12 +100,7 @@ getPath = (callback) -> return callback(error) pathOutput = stdout.replace(/^\s+|\s+$/g, '') - if isAscii(pathOutput) - callback(null, pathOutput) - else - # FIXME Don't corrupt non-ASCII PATH values - # https://github.com/atom/atom/issues/5063 - callback(new Error('PATH contains non-ASCII values')) + callback(null, pathOutput) # Uninstall the Open with Atom explorer context menu items via the registry. uninstallContextMenu = (callback) -> From a26ac5b363ad4c3c187dd337c556c07997bde489 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Mon, 11 Jan 2016 05:32:39 -0700 Subject: [PATCH 005/198] :art: Improve readability of multi-line command --- src/browser/squirrel-update.coffee | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index f9df3c0b5..41e103363 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -58,7 +58,12 @@ spawnReg = (args, callback) -> spawnPowershell = (args, callback) -> # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell - args[0] = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8\r\n$output=#{args[0]}\r\n[Console]::WriteLine($output)" + # to address https://github.com/atom/atom/issues/5063 + args[0] = """ + [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 + $output=#{args[0]} + [Console]::WriteLine($output) + """ args.unshift('-command') args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') From 3a14fa2a2e4b1d34e4c90c7a33da620c1d0acaa5 Mon Sep 17 00:00:00 2001 From: Joe Fitzgerald Date: Tue, 19 Jan 2016 17:41:27 -0800 Subject: [PATCH 006/198] Use ELECTRON_RUN_AS_NODE Variable Key - https://github.com/atom/electron/pull/3594 --- src/buffered-node-process.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffered-node-process.coffee b/src/buffered-node-process.coffee index bb1a1c655..3b4916b24 100644 --- a/src/buffered-node-process.coffee +++ b/src/buffered-node-process.coffee @@ -46,7 +46,7 @@ class BufferedNodeProcess extends BufferedProcess options ?= {} options.env ?= Object.create(process.env) - options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1 + options.env['ELECTRON_RUN_AS_NODE'] = 1 args = args?.slice() ? [] args.unshift(command) From 53693b4d0f741e6246c17171e7812cc5b742b996 Mon Sep 17 00:00:00 2001 From: Arnaud Rinquin Date: Fri, 19 Feb 2016 01:22:48 +0000 Subject: [PATCH 007/198] Add the -a, --add CLI option --- spec/atom-environment-spec.coffee | 8 ++++++++ src/atom-environment.coffee | 4 ++-- src/browser/atom-application.coffee | 21 ++++++++++++--------- src/browser/main.coffee | 4 +++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 0aefe0414..1f8eb08e7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -275,6 +275,14 @@ describe "AtomEnvironment", -> atom.openLocations([{pathToOpen}]) expect(atom.project.getPaths()[0]).toBe __dirname + describe "then a second path is opened with forceAddToWindow", -> + it "adds the second path to the project's paths", -> + firstPathToOpen = __dirname + secondPathToOpen = path.resolve(__dirname, './fixtures') + atom.openLocations([{pathToOpen: firstPathToOpen}]) + atom.openLocations([{pathToOpen: secondPathToOpen, forceAddToWindow: true}]) + expect(atom.project.getPaths()).toEqual([firstPathToOpen, secondPathToOpen]) + describe "when the opened path does not exist but its parent directory does", -> it "adds the parent directory to the project paths", -> pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 74049d94d..304a6300f 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -897,8 +897,8 @@ class AtomEnvironment extends Model openLocations: (locations) -> needsProjectPaths = @project?.getPaths().length is 0 - for {pathToOpen, initialLine, initialColumn} in locations - if pathToOpen? and needsProjectPaths + for {pathToOpen, initialLine, initialColumn, forceAddToWindow} in locations + if pathToOpen? and (needsProjectPaths or forceAddToWindow) if fs.existsSync(pathToOpen) @project.addPath(pathToOpen) else if fs.existsSync(path.dirname(pathToOpen)) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 7ea0342c8..b6d45b2fb 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -85,16 +85,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState}) -> + openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) + @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState}) + @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -409,8 +409,9 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState}) + # :addToLastWindow - Boolean of whether this should be opened in last focused window. + openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> + @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) # Public: Opens multiple paths, in existing windows if possible. # @@ -422,11 +423,12 @@ class AtomApplication # :safeMode - Boolean to control the opened window's safe mode. # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState}={}) -> + # :addToLastWindow - Boolean of whether this should be opened in last focused window. + openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) clearWindowState = Boolean(clearWindowState) - locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom) for pathToOpen in pathsToOpen) + locationsToOpen = (@locationForPathToOpen(pathToOpen, executedFrom, addToLastWindow) for pathToOpen in pathsToOpen) pathsToOpen = (locationToOpen.pathToOpen for locationToOpen in locationsToOpen) unless pidToKillWhenClosed or newWindow @@ -435,6 +437,7 @@ class AtomApplication unless existingWindow? if currentWindow = window ? @lastFocusedWindow existingWindow = currentWindow if ( + addToLastWindow or currentWindow.devMode is devMode and ( stats.every((stat) -> stat.isFile?()) or @@ -603,7 +606,7 @@ class AtomApplication catch error require.resolve(path.resolve(__dirname, '..', '..', 'spec', 'jasmine-test-runner')) - locationForPathToOpen: (pathToOpen, executedFrom='') -> + locationForPathToOpen: (pathToOpen, executedFrom='', forceAddToWindow) -> return {pathToOpen} unless pathToOpen pathToOpen = pathToOpen.replace(/[:\s]+$/, '') @@ -619,7 +622,7 @@ class AtomApplication unless url.parse(pathToOpen).protocol? pathToOpen = path.resolve(executedFrom, fs.normalize(pathToOpen)) - {pathToOpen, initialLine, initialColumn} + {pathToOpen, initialLine, initialColumn, forceAddToWindow} # Opens a native dialog to prompt the user for a path. # diff --git a/src/browser/main.coffee b/src/browser/main.coffee index a65d69306..b4df62bd6 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -132,6 +132,7 @@ parseCommandLine = -> options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).') options.alias('v', 'version').boolean('v').describe('v', 'Print the version information.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') + options.alias('a', 'add').boolean('a').describe('add', 'Open path as a new project in last used window.') options.string('socket-path') options.string('user-data-dir') options.boolean('clear-window-state').describe('clear-window-state', 'Delete all Atom environment state.') @@ -146,6 +147,7 @@ parseCommandLine = -> writeFullVersion() process.exit(0) + addToLastWindow = args['add'] executedFrom = args['executed-from']?.toString() ? process.cwd() devMode = args['dev'] safeMode = args['safe'] @@ -183,6 +185,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath, userDataDir, profileStartup, timeout, setPortable, - clearWindowState} + clearWindowState, addToLastWindow} start() From 0d118afb6b338f9f5b95093498455caf694fdb27 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 12 Feb 2016 15:04:49 -0800 Subject: [PATCH 008/198] Send keyup events through the `keymapManager` --- src/window-event-handler.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index f0d616f19..ce08344c6 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -15,7 +15,8 @@ class WindowEventHandler @addEventListener(@window, 'focus', @handleWindowFocus) @addEventListener(@window, 'blur', @handleWindowBlur) - @addEventListener(@document, 'keydown', @handleDocumentKeydown) + @addEventListener(@document, 'keyup', @handleDocumentKeyEvent) + @addEventListener(@document, 'keydown', @handleDocumentKeyEvent) @addEventListener(@document, 'drop', @handleDocumentDrop) @addEventListener(@document, 'dragover', @handleDocumentDragover) @addEventListener(@document, 'contextmenu', @handleDocumentContextmenu) @@ -66,7 +67,7 @@ class WindowEventHandler target.addEventListener(eventName, handler) @subscriptions.add(new Disposable(-> target.removeEventListener(eventName, handler))) - handleDocumentKeydown: (event) => + handleDocumentKeyEvent: (event) => @atomEnvironment.keymaps.handleKeyboardEvent(event) event.stopImmediatePropagation() From e3015f6d25313e73b446d236deaa9763b81747b1 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 17 Feb 2016 20:46:41 -0800 Subject: [PATCH 009/198] :arrow_up: keybinding-resolver@0.34.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c764111bb..a49def252 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "grammar-selector": "0.48.1", "image-view": "0.56.0", "incompatible-packages": "0.25.1", - "keybinding-resolver": "0.33.0", + "keybinding-resolver": "0.34.0", "line-ending-selector": "0.3.1", "link": "0.31.0", "markdown-preview": "0.157.3", From 4bd3bf4187a1cc927c6104bbbd677f581695676b Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Sun, 21 Feb 2016 13:59:15 -0800 Subject: [PATCH 010/198] :arrow_up: atom-keymap@6.3.1 to support keyup bindings --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a49def252..09bb824cc 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "electronVersion": "0.36.7", "dependencies": { "async": "0.2.6", - "atom-keymap": "^6.2.0", + "atom-keymap": "^6.3.1", "babel-core": "^5.8.21", "bootstrap": "^3.3.4", "cached-run-in-this-context": "0.4.1", From 677568d9aff4359d115b825887e477a31efab28f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 22 Feb 2016 11:30:36 +0100 Subject: [PATCH 011/198] Use `window.requestIdleCallback` in `StateStore.prototype.save` This should alleviate some of the pressure of serializing changes on the main thread. We're assuming that `deadline.timeRemaining()` is high enough to compute the serialization because there's no simple path to serialize state across many `requestIdleCallback`s (e.g. because state might change between two callbacks). --- src/state-store.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/state-store.js b/src/state-store.js index 2175bbe4c..53998d802 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,16 +24,18 @@ class StateStore { } save (key, value) { - return this.dbPromise.then(db => { - if (!db) return + return new Promise((resolve, reject) => { + window.requestIdleCallback(deadline => { + this.dbPromise.then(db => { + if (db == null) resolve() - return new Promise((resolve, reject) => { - var request = db.transaction(['states'], 'readwrite') - .objectStore('states') - .put({value: value, storedAt: new Date().toString()}, key) + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .put({value: value, storedAt: new Date().toString()}, key) - request.onsuccess = resolve - request.onerror = reject + request.onsuccess = resolve + request.onerror = reject + }) }) }) } From a361cd7f40c385a8d34c8a833889f7756e155c12 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 22 Feb 2016 11:44:08 +0100 Subject: [PATCH 012/198] :racehorse: Use JSON.stringify to serialize state --- src/atom-environment.coffee | 15 ++++++++++----- src/state-store.js | 26 ++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 74049d94d..f6fa03932 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -813,12 +813,17 @@ class AtomEnvironment extends Model saveState: -> return Promise.resolve() unless @enablePersistence - state = @serialize() - if storageKey = @getStateKey(@project?.getPaths()) - @stateStore.save(storageKey, state) - else - @applicationDelegate.setTemporaryWindowState(state) + new Promise (resolve, reject) => + window.requestIdleCallback => + state = @serialize() + savePromise = + if storageKey = @getStateKey(@project?.getPaths()) + @stateStore.save(storageKey, state) + else + @applicationDelegate.setTemporaryWindowState(state) + savePromise.catch(reject).then(resolve) + loadState: -> if @enablePersistence diff --git a/src/state-store.js b/src/state-store.js index 53998d802..13c9a0462 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,18 +24,19 @@ class StateStore { } save (key, value) { + // Serialize values using JSON.stringify, as it seems way faster than IndexedDB structured clone. + // (Ref.: https://bugs.chromium.org/p/chromium/issues/detail?id=536620) + let jsonValue = JSON.stringify(value) return new Promise((resolve, reject) => { - window.requestIdleCallback(deadline => { - this.dbPromise.then(db => { - if (db == null) resolve() + this.dbPromise.then(db => { + if (db == null) resolve() - var request = db.transaction(['states'], 'readwrite') - .objectStore('states') - .put({value: value, storedAt: new Date().toString()}, key) + var request = db.transaction(['states'], 'readwrite') + .objectStore('states') + .put({value: jsonValue, storedAt: new Date().toString(), isJSON: true}, key) - request.onsuccess = resolve - request.onerror = reject - }) + request.onsuccess = resolve + request.onerror = reject }) }) } @@ -51,7 +52,12 @@ class StateStore { request.onsuccess = (event) => { let result = event.target.result - resolve(result ? result.value : null) + if (result) { + // TODO: remove this when state will be serialized only via JSON. + resolve(result.isJSON ? JSON.parse(result.value) : result.value) + } else { + resolve(null) + } } request.onerror = (event) => reject(event) From bb1c048e12d5a3930b4c536d3a3ac453673ac4f2 Mon Sep 17 00:00:00 2001 From: Arnaud Rinquin Date: Mon, 22 Feb 2016 18:37:20 +0000 Subject: [PATCH 013/198] Add -a, --add specific integration tests --- spec/integration/startup-spec.coffee | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/spec/integration/startup-spec.coffee b/spec/integration/startup-spec.coffee index e82d34e57..f6b0e1cf3 100644 --- a/spec/integration/startup-spec.coffee +++ b/spec/integration/startup-spec.coffee @@ -123,6 +123,34 @@ describe "Starting Atom", -> .waitForPaneItemCount(0, 1000) .treeViewRootDirectories() .then ({value}) -> expect(value).toEqual([otherTempDirPath]) + describe "when using the -a, --add option", -> + it "reuses that window and add the folder to project paths", -> + fourthTempDir = temp.mkdirSync("a-fourth-dir") + fourthTempFilePath = path.join(fourthTempDir, "a-file") + fs.writeFileSync(fourthTempFilePath, "4 - This file was already here.") + + fifthTempDir = temp.mkdirSync("a-fifth-dir") + fifthTempFilePath = path.join(fifthTempDir, "a-file") + fs.writeFileSync(fifthTempFilePath, "5 - This file was already here.") + + runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) -> + client + .waitForPaneItemCount(1, 5000) + + # Opening another file reuses the same window and add parent dir to + # project paths. + .startAnotherAtom(['-a', fourthTempFilePath], ATOM_HOME: atomHome) + .waitForPaneItemCount(2, 5000) + .waitForWindowCount(1, 1000) + .treeViewRootDirectories() + .then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir]) + .execute -> atom.workspace.getActiveTextEditor().getText() + .then ({value: text}) -> expect(text).toBe "4 - This file was already here." + + # Opening another directory resuses the same window and add the folder to project paths. + .startAnotherAtom(['--add', fifthTempDir], ATOM_HOME: atomHome) + .treeViewRootDirectories() + .then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir, fifthTempDir]) it "opens the new window offset from the other window", -> runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) -> From 6457e0573edf783ca5d95a73e2df7b4a36ed13b6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 22 Feb 2016 13:28:00 -0800 Subject: [PATCH 014/198] :arrow_up: keybinding-resolver to fix specs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09bb824cc..58a17c648 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "grammar-selector": "0.48.1", "image-view": "0.56.0", "incompatible-packages": "0.25.1", - "keybinding-resolver": "0.34.0", + "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", "link": "0.31.0", "markdown-preview": "0.157.3", From ea5500a124945b06fc9ca70f6924f860e097643e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 22 Feb 2016 16:31:06 -0800 Subject: [PATCH 015/198] Print line number of wait timeout in presenter spec --- spec/text-editor-presenter-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 57c674033..543f8349d 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -91,7 +91,9 @@ describe "TextEditorPresenter", -> expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn) waitsForStateToUpdate = (presenter, fn) -> - waitsFor "presenter state to update", 1000, (done) -> + line = new Error().stack.split('\n')[2].split(':')[1] + + waitsFor "presenter state to update at line #{line}", 1000, (done) -> disposable = presenter.onDidUpdateState -> disposable.dispose() process.nextTick(done) From 98559f5d342a5a7e37ab6108ae747eab893cf9c9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 22 Feb 2016 16:31:17 -0800 Subject: [PATCH 016/198] Add missing --- spec/text-editor-presenter-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 543f8349d..05ac87c0c 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1340,7 +1340,9 @@ describe "TextEditorPresenter", -> blockDecoration3 = addBlockDecorationBeforeScreenRow(7) blockDecoration4 = null - waitsForStateToUpdate presenter, blockDecoration4 = addBlockDecorationAfterScreenRow(7) + waitsForStateToUpdate presenter, -> + blockDecoration4 = addBlockDecorationAfterScreenRow(7) + runs -> expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1]) expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([]) From 8896f15b5d436370edd07c4a84aae0b604a129a2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 12:26:22 -0800 Subject: [PATCH 017/198] :arrow_up: autocomplete-plus@2.29.0 for Unicode support --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b430d4316..4c5e13a03 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.28.0", + "autocomplete-plus": "2.29.0", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 70efaf4cdde1080280f15b7729887fb0e751e0a4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 16:04:37 -0800 Subject: [PATCH 018/198] :arrow_up: notifications@0.62.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c5e13a03..d3d2dc945 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.0", "markdown-preview": "0.157.3", "metrics": "0.53.1", - "notifications": "0.62.2", + "notifications": "0.62.3", "open-on-github": "1.0.0", "package-generator": "0.41.1", "settings-view": "0.232.4", From 5612f1a46c4d3237c20f7239a9d606b27ee11e02 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 24 Feb 2016 14:42:09 +0100 Subject: [PATCH 019/198] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d3d2dc945..db472e5ae 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.1", + "text-buffer": "8.3.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 2853c2999ccfa60d725f7c3eba4f51caef4fcc45 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 24 Feb 2016 12:59:44 -0800 Subject: [PATCH 020/198] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db472e5ae..ae923f99c 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.2", - "fuzzy-finder": "1.0.1", + "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From a487110521ef5f546f5d5b6c7be4ab3d6afe83ab Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 24 Feb 2016 18:19:13 -0800 Subject: [PATCH 021/198] Refactor pending state to live in pane instead of items * New public API `workspace.setItemNotPending` that packages can use to set an item to set an item to not pending (e.g. when the user interacts with the item) * Pending state for newly opened items with `{pending: true}` is now tracked by `Pane` instead of the item, and packages like `tabs` that query this information now get it from the Pane. --- src/pane.coffee | 37 +++++++++++++++++++++++++++---------- src/text-editor.coffee | 21 ++++++--------------- src/workspace.coffee | 10 ++++++++-- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 9dff81d5c..0dec5cbce 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -53,7 +53,7 @@ class Pane extends Model items: compact(@items.map((item) -> item.serialize?())) activeItemURI: activeItemURI focused: @focused - flexScale: @flexScale + flexScale: @flexScale # TODO: is it okay to not serialize pending state? does it need to be restored? getParent: -> @parent @@ -342,13 +342,15 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. - activateItem: (item) -> + # + # * `pending` TODO + activateItem: (item, pending=false) -> if item? - if @activeItem?.isPending?() + if @isItemPending(@activeItem) index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 - @addItem(item, index, false) + @addItem(item, index, false, pending) @setActiveItem(item) # Public: Add the given item to the pane. @@ -357,19 +359,18 @@ class Pane extends Model # view. # * `index` (optional) {Number} indicating the index at which to add the item. # If omitted, the item is added after the current active item. + # * `pending` TODO # # Returns the added item. - addItem: (item, index=@getActiveItemIndex() + 1, moved=false) -> + addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object' throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?() return if item in @items - if item.isPending?() - for existingItem, i in @items - if existingItem.isPending?() - @destroyItem(existingItem) - break + pendingItem = @getPendingItem() + @destroyItem(pendingItem) if pendingItem? + @setPendingItem(item) if pending if typeof item.onDidDestroy is 'function' @itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, false) @@ -379,6 +380,20 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item + setPendingItem: (item) => + @pendingItem = item + @emitter.emit 'did-terminate-pending-state' if not item + + getPendingItem: => + @pendingItem + + isItemPending: (item) => + @pendingItem is item + + onDidTerminatePendingState: (callback) => + @emitter.on 'did-terminate-pending-state', -> + callback() + # Public: Add the given items to the pane. # # * `items` An {Array} of items to add. Items can be views or models with @@ -397,6 +412,8 @@ class Pane extends Model index = @items.indexOf(item) return if index is -1 + @pendingItem = null if @isItemPending(item) + @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..e8207ca1e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -92,7 +92,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending + @project, @assert, @applicationDelegate } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -111,6 +111,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] + @bufferHasChanged = false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -151,7 +152,7 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id - pending: @isPending() + bufferHasChanged: @bufferHasChanged subscribeToBuffer: -> @buffer.retain() @@ -163,9 +164,9 @@ class TextEditor extends Model @disposables.add @buffer.onDidChangeEncoding => @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() - if @pending - @disposables.add @buffer.onDidChangeModified => - @terminatePendingState() if @buffer.isModified() + @disposables.add @buffer.onDidChangeModified => + atom.workspace.setItemNotPending(this) if not @bufferHasChanged and @buffer.isModified() + @bufferHasChanged = true @preserveCursorPositionOnBufferReload() @@ -575,13 +576,6 @@ class TextEditor extends Model getEditorWidthInChars: -> @displayBuffer.getEditorWidthInChars() - onDidTerminatePendingState: (callback) -> - @emitter.on 'did-terminate-pending-state', callback - - terminatePendingState: -> - return if not @pending - @pending = false - @emitter.emit 'did-terminate-pending-state' ### Section: File Details @@ -666,9 +660,6 @@ class TextEditor extends Model # Essential: Returns {Boolean} `true` if this editor has no content. isEmpty: -> @buffer.isEmpty() - # Returns {Boolean} `true` if this editor is pending and `false` if it is permanent. - isPending: -> Boolean(@pending) - # Copies the current file path to the native clipboard. copyPathToClipboard: (relative = false) -> if filePath = @getPath() diff --git a/src/workspace.coffee b/src/workspace.coffee index 0bfff7e0f..1f6648271 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -477,7 +477,7 @@ class Workspace extends Model if uri? if item = pane.itemForURI(uri) - item.terminatePendingState?() if item.isPending?() and not options.pending + pane.setPendingItem(null) if not options.pending item ?= opener(uri, options) for opener in @getOpeners() when not item try @@ -500,7 +500,7 @@ class Workspace extends Model return item if pane.isDestroyed() @itemOpened(item) - pane.activateItem(item) if activateItem + pane.activateItem(item, options.pending) if activateItem pane.activate() if activatePane initialLine = initialColumn = 0 @@ -515,6 +515,12 @@ class Workspace extends Model @emitter.emit 'did-open', {uri, pane, item, index} item + setItemNotPending: (item) => + for pane in @getPanes() + if item is pane.getPendingItem() + pane.setPendingItem(null) + break + openTextFile: (uri, options) -> filePath = @project.resolvePath(uri) From e94653d391bfeeebf7c1f424d04930f98d0fe584 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 24 Feb 2016 20:29:49 -0800 Subject: [PATCH 022/198] :arrow_up: tree-view@0.201.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae923f99c..a9eab6b3b 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.2", "timecop": "0.33.1", - "tree-view": "0.201.3", + "tree-view": "0.201.4", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 24865fd254353b5ca854d4b8ed5807adcfcc691b Mon Sep 17 00:00:00 2001 From: Alfred UC Date: Thu, 25 Feb 2016 20:30:25 +0900 Subject: [PATCH 023/198] Fix a inconsistent getLineCount() use --- src/text-editor.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..9c93f4925 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3215,7 +3215,7 @@ class TextEditor extends Model # top of the visible area. setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView - maxScreenRow = @getLineCount() - 1 + maxScreenRow = @getScreenLineCount() - 1 unless @config.get('editor.scrollPastEnd') height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() @@ -3233,7 +3233,7 @@ class TextEditor extends Model height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? - Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getLineCount() - 1) + Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getScreenLineCount() - 1) else null From a62965dc62336f8b344eefc20c8a0440edf69435 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 09:25:13 -0800 Subject: [PATCH 024/198] Default the channel based on the package.json, not the branch --- build/Gruntfile.coffee | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index a34e1fbd4..2340f8823 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -35,22 +35,8 @@ module.exports = (grunt) -> # Options installDir = grunt.option('install-dir') - buildDir = grunt.option('build-dir') - buildDir ?= 'out' - buildDir = path.resolve(buildDir) - - channel = grunt.option('channel') - releasableBranches = ['stable', 'beta'] - if process.env.APPVEYOR and not process.env.APPVEYOR_PULL_REQUEST_NUMBER - channel ?= process.env.APPVEYOR_REPO_BRANCH if process.env.APPVEYOR_REPO_BRANCH in releasableBranches - - if process.env.TRAVIS and not process.env.TRAVIS_PULL_REQUEST - channel ?= process.env.TRAVIS_BRANCH if process.env.TRAVIS_BRANCH in releasableBranches - - if process.env.JANKY_BRANCH - channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in releasableBranches - - channel ?= 'dev' + buildDir = path.resolve(grunt.option('build-dir') ? 'out') + channel = grunt.option('channel') ? getDefaultReleaseChannel() metadata = packageJson appName = packageJson.productName @@ -310,3 +296,15 @@ module.exports = (grunt) -> unless process.platform is 'linux' or grunt.option('no-install') defaultTasks.push 'install' grunt.registerTask('default', defaultTasks) + +getDefaultReleaseChannel = -> + {version} = packageJson + if version.match(/dev/) or isBuildingPR() + 'dev' + else if version.match(/beta/) + 'beta' + else + 'stable' + +isBuildingPR = -> + process.env.APPVEYOR_PULL_REQUEST_NUMBER? or process.env.TRAVIS_PULL_REQUEST? From 77bae0fad1df9be04d2f98ad05e49bf099a6dcbb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 10:24:41 -0800 Subject: [PATCH 025/198] When creating draft releases, choose release branch based on version number --- build/Gruntfile.coffee | 22 ++++++++++++++-------- build/tasks/publish-build-task.coffee | 13 ++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 2340f8823..d9375d05c 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -34,9 +34,10 @@ module.exports = (grunt) -> grunt.file.setBase(path.resolve('..')) # Options + [defaultChannel, releaseBranch] = getDefaultChannelAndReleaseBranch(packageJson.version) installDir = grunt.option('install-dir') buildDir = path.resolve(grunt.option('build-dir') ? 'out') - channel = grunt.option('channel') ? getDefaultReleaseChannel() + channel = grunt.option('channel') ? defaultChannel metadata = packageJson appName = packageJson.productName @@ -175,7 +176,7 @@ module.exports = (grunt) -> pkg: grunt.file.readJSON('package.json') atom: { - appName, channel, metadata, + appName, channel, metadata, releaseBranch, appFileName, apmFileName, appDir, buildDir, contentsDir, installDir, shellAppDir, symbolsDir, } @@ -297,14 +298,19 @@ module.exports = (grunt) -> defaultTasks.push 'install' grunt.registerTask('default', defaultTasks) -getDefaultReleaseChannel = -> - {version} = packageJson +getDefaultChannelAndReleaseBranch = (version) -> if version.match(/dev/) or isBuildingPR() - 'dev' - else if version.match(/beta/) - 'beta' + channel = 'dev' + releaseBranch = null else - 'stable' + if version.match(/beta/) + channel = 'beta' + else + channel = 'stable' + + minorVersion = version.match(/^\d\.\d/)[0] + releaseBranch = "#{minorVersion}-releases" + [channel, releaseBranch] isBuildingPR = -> process.env.APPVEYOR_PULL_REQUEST_NUMBER? or process.env.TRAVIS_PULL_REQUEST? diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 4f8df6336..de46eb4fe 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -31,14 +31,9 @@ module.exports = (gruntObject) -> cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json') grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', -> - channel = grunt.config.get('atom.channel') - switch channel - when 'stable' - isPrerelease = false - when 'beta' - isPrerelease = true - else - return + releaseBranch = grunt.config.get('atom.releaseBranch') + isPrerelease = grunt.config.get('atom.channel') is 'beta' + return unless releaseBranch? doneCallback = @async() startTime = Date.now() @@ -55,7 +50,7 @@ module.exports = (gruntObject) -> zipAssets buildDir, assets, (error) -> return done(error) if error? - getAtomDraftRelease isPrerelease, channel, (error, release) -> + getAtomDraftRelease isPrerelease, releaseBranch, (error, release) -> return done(error) if error? assetNames = (asset.assetName for asset in assets) deleteExistingAssets release, assetNames, (error) -> From 9c5c171eb59b9d7a17668c6b9bf25477523b09da Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 25 Feb 2016 10:28:03 -0800 Subject: [PATCH 026/198] Don't refer to stable and beta as branches --- build/tasks/set-version-task.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/tasks/set-version-task.coffee b/build/tasks/set-version-task.coffee index fc2382476..c7a29b584 100644 --- a/build/tasks/set-version-task.coffee +++ b/build/tasks/set-version-task.coffee @@ -5,9 +5,7 @@ module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) getVersion = (callback) -> - releasableBranches = ['stable', 'beta'] - channel = grunt.config.get('atom.channel') - shouldUseCommitHash = if channel in releasableBranches then false else true + shouldUseCommitHash = grunt.config.get('atom.channel') is 'dev' inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git')) {version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json')) if shouldUseCommitHash and inRepository From d0ffbca845ca1f2f966b5f0b950c7b147c6e233a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 25 Feb 2016 10:49:11 -0800 Subject: [PATCH 027/198] :lipstick: and :memo: for pending state --- src/pane.coffee | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 0dec5cbce..a01703ce5 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -8,6 +8,11 @@ TextEditor = require './text-editor' # Panes can contain multiple items, one of which is *active* at a given time. # The view corresponding to the active item is displayed in the interface. In # the default configuration, tabs are also displayed for each item. +# +# Each pane may also contain one *pending* item. When a pending item is added +# to a pane, it will replace the currently pending item, if any, instead of +# simply being added. In the default configuration, the text in the tab for +# pending items is shown in italics. module.exports = class Pane extends Model container: undefined @@ -380,19 +385,47 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item + clearPendingItem: => + @setPendingItem(null) + setPendingItem: (item) => + # TODO: figure out events for changing/clearing pending item @pendingItem = item + @emitter.emit 'did-change-pending-item', @pendingItem @emitter.emit 'did-terminate-pending-state' if not item + # Public: Get the pending pane item in this pane, if any. + # + # Returns a pane item or `null`. getPendingItem: => - @pendingItem + @pendingItem or null isItemPending: (item) => @pendingItem is item + # Invoke the given callback when the value of {::getPendingItem} changes. + # + # * `callback` {Function} to be called with when the pending item changes. + # * `pendingItem` The current pending item, or `null`. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangePendingItem: (callback) => + @emitter.on 'did-change-pending-item', callback + + # Public: Invoke the given callback with the current and future values of + # {::getPendingItem}. + # + # * `callback` {Function} to be called with the current and future pending + # items. + # * `pendingItem` The current pending item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePendingItem: (callback) -> + callback(@getPendingItem()) + @onDidChangePendingItem(callback) + onDidTerminatePendingState: (callback) => - @emitter.on 'did-terminate-pending-state', -> - callback() + @emitter.on 'did-terminate-pending-state', callback # Public: Add the given items to the pane. # From 44dabf1e2fb6f058d7d2e024063f036bf75bc73b Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:22:10 -0500 Subject: [PATCH 028/198] Added .getElement to TextEditor. --- src/text-editor.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c0a6f2057..d1c092598 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -11,6 +11,7 @@ Selection = require './selection' TextMateScopeSelector = require('first-mate').ScopeSelector {Directory} = require "pathwatcher" GutterContainer = require './gutter-container' +TextEditorElement = require './text-editor-element' # Essential: This class represents all essential editing state for a single # {TextBuffer}, including cursor and selection positions, folds, and soft wraps. @@ -61,6 +62,10 @@ class TextEditor extends Model suppressSelectionMerging: false selectionFlashDuration: 500 gutterContainer: null + editorElement: null + + Object.defineProperty @prototype, "element", + get: -> @getElement() @deserialize: (state, atomEnvironment) -> try @@ -3142,6 +3147,10 @@ class TextEditor extends Model Section: TextEditor Rendering ### + # Get the Element for the editor. + getElement: -> + @editorElement ?= new TextEditorElement().initialize(this, atom) + # Essential: Retrieves the greyed out placeholder of a mini editor. # # Returns a {String}. From 10f0064a63c93b36511ce265453e6394f4e594e4 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:31 -0500 Subject: [PATCH 029/198] Call .getElement if the model has it. --- src/view-registry.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/view-registry.coffee b/src/view-registry.coffee index ef7151353..5fbfba729 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -171,6 +171,11 @@ class ViewRegistry if object instanceof HTMLElement return object + if typeof object?.getElement is 'function' + element = object.getElement() + if element instanceof HTMLElement + return element + if object?.element instanceof HTMLElement return object.element From 02c7bb3ddd8ef2980e5c7be69c2dfb0e78967370 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:38 -0500 Subject: [PATCH 030/198] Don't need this view provider anymore. --- src/atom-environment.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index c42bf05aa..dc1bef7d4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -254,8 +254,6 @@ class AtomEnvironment extends Model new PaneAxisElement().initialize(model, env) @views.addViewProvider Pane, (model, env) -> new PaneElement().initialize(model, env) - @views.addViewProvider TextEditor, (model, env) -> - new TextEditorElement().initialize(model, env) @views.addViewProvider(Gutter, createGutterView) registerDefaultOpeners: -> From dfb1d1d62d091bfc6e87fb3e56bd2c9b81d36d89 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 10:24:51 -0500 Subject: [PATCH 031/198] Expose a bound buildTextEditor. --- src/workspace.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 0bfff7e0f..e0a913f94 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -43,6 +43,12 @@ class Workspace extends Model @defaultDirectorySearcher = new DefaultDirectorySearcher() @consumeServices(@packageManager) + # One cannot simply .bind here since it could be used as a component with + # Etch, which means it'd be `new`d in which case `this` would the new + # object. + realThis = this + @buildTextEditor = (params) -> realThis.buildTextEditor_(params) + @panelContainers = top: new PanelContainer({location: 'top'}) left: new PanelContainer({location: 'left'}) @@ -550,7 +556,7 @@ class Workspace extends Model # Extended: Create a new text editor. # # Returns a {TextEditor}. - buildTextEditor: (params) -> + buildTextEditor_: (params) -> params = _.extend({ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate From 768f5ee5ca1c9eb2a896e668661c54e6dbc1541f Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 14:00:49 -0500 Subject: [PATCH 032/198] Maybe a better comment? --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index e0a913f94..15ffc6e6a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -44,8 +44,8 @@ class Workspace extends Model @consumeServices(@packageManager) # One cannot simply .bind here since it could be used as a component with - # Etch, which means it'd be `new`d in which case `this` would the new - # object. + # Etch, in which case it'd be `new`d. And when it's `new`d, `this` is always + # the newly created object. realThis = this @buildTextEditor = (params) -> realThis.buildTextEditor_(params) From 55e1496b96b93d9cec2a48dd261081165b09632e Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 14:49:07 -0500 Subject: [PATCH 033/198] Call the prototype method directly. h/t @maxbrunsfeld --- src/workspace.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index 15ffc6e6a..31c381428 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -47,7 +47,7 @@ class Workspace extends Model # Etch, in which case it'd be `new`d. And when it's `new`d, `this` is always # the newly created object. realThis = this - @buildTextEditor = (params) -> realThis.buildTextEditor_(params) + @buildTextEditor = -> Workspace.prototype.buildTextEditor.apply(realThis, arguments) @panelContainers = top: new PanelContainer({location: 'top'}) @@ -556,7 +556,7 @@ class Workspace extends Model # Extended: Create a new text editor. # # Returns a {TextEditor}. - buildTextEditor_: (params) -> + buildTextEditor: (params) -> params = _.extend({ @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate From f3ce468a7068a09c26e2f76078ad5a820253cf9b Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:01:40 -0500 Subject: [PATCH 034/198] Support specifying whether to ignore invisibles and the grammar. --- src/text-editor.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d1c092598..e1706d5f1 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -119,7 +119,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini || ignoreInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer @@ -148,6 +148,9 @@ class TextEditor extends Model priority: 0 visible: lineNumberGutterVisible + if grammarName? + @setGrammar(@grammarRegistry.grammarForScopeName(grammarName)) + serialize: -> deserializer: 'TextEditor' id: @id From 822cd780555278b615e0da32020115b0d5fe7295 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:18:29 -0500 Subject: [PATCH 035/198] Use the computed style to find the height --- src/text-editor-component.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index bdd0befcd..d5103c1fb 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -731,8 +731,7 @@ class TextEditorComponent measureDimensions: -> return unless @mounted - {position} = getComputedStyle(@hostElement) - {height} = @hostElement.style + {position, height} = getComputedStyle(@hostElement) if position is 'absolute' or height @presenter.setAutoHeight(false) From 2af53231f16c602423f223fe99fb834a730d5f9f Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:21:56 -0500 Subject: [PATCH 036/198] Less lint. --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e1706d5f1..19b7e8c86 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -119,7 +119,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini || ignoreInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or ignoreInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From 1aa0cec4115d2d3ced6f7059f23676093ac1c7bb Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:24:22 -0500 Subject: [PATCH 037/198] :arrow_up: nodegit@0.11.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a9eab6b3b..c73fdec8a 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.9.0", + "nodegit": "0.11.5", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From dd780a7c5a415f513acae6d501df1f4f23c1cf93 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 15:41:55 -0500 Subject: [PATCH 038/198] Revert "Use the computed style to find the height" This reverts commit 822cd780555278b615e0da32020115b0d5fe7295. --- src/text-editor-component.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index d5103c1fb..bdd0befcd 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -731,7 +731,8 @@ class TextEditorComponent measureDimensions: -> return unless @mounted - {position, height} = getComputedStyle(@hostElement) + {position} = getComputedStyle(@hostElement) + {height} = @hostElement.style if position is 'absolute' or height @presenter.setAutoHeight(false) From dd83619c45a8fc0e4decf0127bfa38d0f46a128d Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 16:05:57 -0500 Subject: [PATCH 039/198] Add autoHeight setting. --- src/text-editor-element.coffee | 4 ++++ src/text-editor.coffee | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 380417163..554f73cef 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -344,6 +344,10 @@ class TextEditorElement extends HTMLElement @style.height = height + "px" @component.measureDimensions() + disableAutoHeight: -> + @style.height = "100%" + @component.measureDimensions() + getHeight: -> @offsetHeight diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 19b7e8c86..06e58aa1e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -116,6 +116,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] + @autoHeight ?= true buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -3152,7 +3153,11 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - @editorElement ?= new TextEditorElement().initialize(this, atom) + if not @editorElement? + @editorElement = new TextEditorElement().initialize(this, atom) + if not @autoHeight + @editorElement.disableAutoHeight() + @editorElement # Essential: Retrieves the greyed out placeholder of a mini editor. # From ff0b9e30a9bea281bfad8ab55e57bce4daf5e386 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 16:59:58 -0500 Subject: [PATCH 040/198] Add ignoreScrollPastEnd --- src/text-editor-component.coffee | 3 ++- src/text-editor-element.coffee | 4 +++- src/text-editor-presenter.coffee | 4 ++-- src/text-editor.coffee | 11 ++++++----- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index bdd0befcd..5a4097fc5 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -43,7 +43,7 @@ class TextEditorComponent @assert domNode?, "TextEditorComponent::domNode was set to null." @domNodeValue = domNode - constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars}) -> + constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, ignoreScrollPastEnd}) -> @tileSize = tileSize if tileSize? @disposables = new CompositeDisposable @@ -61,6 +61,7 @@ class TextEditorComponent stoppedScrollingDelay: 200 config: @config lineTopIndex: lineTopIndex + ignoreScrollPastEnd: ignoreScrollPastEnd @presenter.onDidUpdateState(@requestUpdate) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 554f73cef..6fad33dc3 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -17,6 +17,7 @@ class TextEditorElement extends HTMLElement focusOnAttach: false hasTiledRendering: true logicalDisplayBuffer: true + ignoreScrollPastEnd: false createdCallback: -> # Use globals when the following instance variables aren't set. @@ -86,7 +87,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @ignoreScrollPastEnd = false) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -143,6 +144,7 @@ class TextEditorElement extends HTMLElement workspace: @workspace assert: @assert grammars: @grammars + ignoreScrollPastEnd: @ignoreScrollPastEnd ) @rootElement.appendChild(@component.getDomNode()) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index a3504caa8..0db175c2b 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -13,7 +13,7 @@ class TextEditorPresenter minimumReflowInterval: 200 constructor: (params) -> - {@model, @config, @lineTopIndex} = params + {@model, @config, @lineTopIndex, @ignoreScrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params @@ -661,7 +661,7 @@ class TextEditorPresenter return unless @contentHeight? and @clientHeight? contentHeight = @contentHeight - if @scrollPastEnd + if @scrollPastEnd and not @ignoreScrollPastEnd extraScrollHeight = @clientHeight - (@lineHeight * 3) contentHeight += extraScrollHeight if extraScrollHeight > 0 scrollHeight = Math.max(contentHeight, @height) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c1a4150e9..c22a1a73f 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -97,7 +97,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight + @project, @assert, @applicationDelegate, @pending, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -117,6 +117,7 @@ class TextEditor extends Model @cursorsByMarkerId = new Map @selections = [] @autoHeight ?= true + @ignoreScrollPastEnd ?= false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -3153,9 +3154,9 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - if not @editorElement? - @editorElement = new TextEditorElement().initialize(this, atom) - if not @autoHeight + unless @editorElement? + @editorElement = new TextEditorElement().initialize(this, atom, @ignoreScrollPastEnd) + unless @autoHeight @editorElement.disableAutoHeight() @editorElement @@ -3233,7 +3234,7 @@ class TextEditor extends Model setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView maxScreenRow = @getScreenLineCount() - 1 - unless @config.get('editor.scrollPastEnd') + unless @config.get('editor.scrollPastEnd') and not @ignoreScrollPastEnd height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? From dfd3e1b94840de8eecd89aa9793bc32f4260037c Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 25 Feb 2016 17:11:04 -0500 Subject: [PATCH 041/198] Take autoHeight as an argument. --- src/text-editor-element.coffee | 9 ++++----- src/text-editor.coffee | 6 +----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 6fad33dc3..02f688e2c 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -39,6 +39,9 @@ class TextEditorElement extends HTMLElement @setAttribute('tabindex', -1) initializeContent: (attributes) -> + unless @autoHeight + @style.height = "100%" + if @config.get('editor.useShadowDOM') @useShadowDOM = true @@ -87,7 +90,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @ignoreScrollPastEnd = false) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @ignoreScrollPastEnd = false) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -346,10 +349,6 @@ class TextEditorElement extends HTMLElement @style.height = height + "px" @component.measureDimensions() - disableAutoHeight: -> - @style.height = "100%" - @component.measureDimensions() - getHeight: -> @offsetHeight diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c22a1a73f..2823b993e 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -3154,11 +3154,7 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - unless @editorElement? - @editorElement = new TextEditorElement().initialize(this, atom, @ignoreScrollPastEnd) - unless @autoHeight - @editorElement.disableAutoHeight() - @editorElement + @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @ignoreScrollPastEnd) # Essential: Retrieves the greyed out placeholder of a mini editor. # From b637366a58c97c0d3c98a55db8b91ed1c6482664 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 16:09:40 -0800 Subject: [PATCH 042/198] Workspace#setItemNotPending :arrow_right: Item#onDidTerminatePendingState Signed-off-by: Michelle Tilley --- src/pane.coffee | 16 +++++++++++----- src/text-editor.coffee | 6 ++++-- src/workspace.coffee | 6 ------ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index a01703ce5..16c2f79b2 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,5 +1,5 @@ {find, compact, extend, last} = require 'underscore-plus' -{Emitter} = require 'event-kit' +{CompositeDisposable, Emitter} = require 'event-kit' Model = require './model' PaneAxis = require './pane-axis' TextEditor = require './text-editor' @@ -42,7 +42,7 @@ class Pane extends Model } = params @emitter = new Emitter - @itemSubscriptions = new WeakMap + @subscriptionsPerItem = new WeakMap @items = [] @addItems(compact(params?.items ? [])) @@ -265,8 +265,8 @@ class Pane extends Model getPanes: -> [this] unsubscribeFromItem: (item) -> - @itemSubscriptions.get(item)?.dispose() - @itemSubscriptions.delete(item) + @subscriptionsPerItem.get(item)?.dispose() + @subscriptionsPerItem.delete(item) ### Section: Items @@ -378,7 +378,13 @@ class Pane extends Model @setPendingItem(item) if pending if typeof item.onDidDestroy is 'function' - @itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, false) + itemSubscriptions = new CompositeDisposable + itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) + if typeof item.onDidTerminatePendingState is "function" + itemSubscriptions.add item.onDidTerminatePendingState => + @clearPendingItem() if @getPendingItem() is item + itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) + @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) @emitter.emit 'did-add-item', {item, index, moved} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e8207ca1e..53889e4c9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -165,11 +165,13 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - atom.workspace.setItemNotPending(this) if not @bufferHasChanged and @buffer.isModified() - @bufferHasChanged = true + @emitter.emit 'did-terminate-pending-state' @preserveCursorPositionOnBufferReload() + onDidTerminatePendingState: (callback) -> + @emitter.on 'did-terminate-pending-state', callback + subscribeToDisplayBuffer: -> @disposables.add @selectionsMarkerLayer.onDidCreateMarker @addSelection.bind(this) @disposables.add @displayBuffer.onDidChangeGrammar @handleGrammarChange.bind(this) diff --git a/src/workspace.coffee b/src/workspace.coffee index 1f6648271..39ec2c599 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -515,12 +515,6 @@ class Workspace extends Model @emitter.emit 'did-open', {uri, pane, item, index} item - setItemNotPending: (item) => - for pane in @getPanes() - if item is pane.getPendingItem() - pane.setPendingItem(null) - break - openTextFile: (uri, options) -> filePath = @project.resolvePath(uri) From 1c65d0e5e4acd3192e437012760145d00e650ee0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 16:48:16 -0800 Subject: [PATCH 043/198] Changed Pane and TextEditor specs to match new pending behavior --- spec/pane-spec.coffee | 81 ++++++++++++++++++++++++++++++------ spec/text-editor-spec.coffee | 60 -------------------------- src/text-editor.coffee | 9 ++-- 3 files changed, 75 insertions(+), 75 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 8c228e2a8..62971bfd1 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -18,8 +18,8 @@ describe "Pane", -> onDidDestroy: (fn) -> @emitter.on('did-destroy', fn) destroy: -> @destroyed = true; @emitter.emit('did-destroy') isDestroyed: -> @destroyed - isPending: -> @pending - pending: false + onDidTerminatePendingState: (callback) -> @emitter.on 'terminate-pending-state', callback + terminatePendingState: -> @emitter.emit 'terminate-pending-state' beforeEach -> confirm = spyOn(atom.applicationDelegate, 'confirm') @@ -136,10 +136,8 @@ describe "Pane", -> pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") - itemA.pending = true - itemB.pending = true - pane.addItem(itemA) - pane.addItem(itemB) + pane.addItem(itemA, undefined, false, true) + pane.addItem(itemB, undefined, false, true) expect(itemA.isDestroyed()).toBe true describe "::activateItem(item)", -> @@ -172,19 +170,17 @@ describe "Pane", -> beforeEach -> itemC = new Item("C") itemD = new Item("D") - itemC.pending = true - itemD.pending = true it "replaces the active item if it is pending", -> - pane.activateItem(itemC) + pane.activateItem(itemC, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B'] - pane.activateItem(itemD) + pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B'] it "adds the item after the active item if it is not pending", -> - pane.activateItem(itemC) + pane.activateItem(itemC, true) pane.activateItemAtIndex(2) - pane.activateItem(itemD) + pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] describe "::activateNextItem() and ::activatePreviousItem()", -> @@ -806,6 +802,67 @@ describe "Pane", -> pane2.destroy() expect(container.root).toBe pane1 + describe "pending state", -> + editor1 = null + pane = null + eventCount = null + + beforeEach -> + waitsForPromise -> + atom.workspace.open('sample.txt', pending: true).then (o) -> + editor1 = o + pane = atom.workspace.getActivePane() + + runs -> + eventCount = 0 + editor1.onDidTerminatePendingState -> eventCount++ + + it "does not open file in pending state by default", -> + waitsForPromise -> + atom.workspace.open('sample.js').then (o) -> + editor1 = o + pane = atom.workspace.getActivePane() + + runs -> + expect(pane.getPendingItem()).toBeNull() + + it "opens file in pending state if 'pending' option is true", -> + expect(pane.getPendingItem()).toEqual editor1 + + it "terminates pending state if ::terminatePendingState is invoked", -> + editor1.terminatePendingState() + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "terminates pending state when buffer is changed", -> + editor1.insertText('I\'ll be back!') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "only calls terminate handler once when text is modified twice", -> + editor1.insertText('Some text') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + editor1.save() + + editor1.insertText('More text') + advanceClock(editor1.getBuffer().stoppedChangingDelay) + + expect(pane.getPendingItem()).toBeNull() + expect(eventCount).toBe 1 + + it "only calls clearPendingItem if there is a pending item to clear", -> + spyOn(pane, "clearPendingItem").andCallThrough() + + editor1.terminatePendingState() + editor1.terminatePendingState() + + expect(pane.getPendingItem()).toBeNull() + expect(pane.clearPendingItem.callCount).toBe 1 + describe "serialization", -> pane = null diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 6959d4da5..6f16f0cf7 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -55,16 +55,6 @@ describe "TextEditor", -> expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?' - it "restores pending tabs in pending state", -> - expect(editor.isPending()).toBe false - editor2 = TextEditor.deserialize(editor.serialize(), atom) - expect(editor2.isPending()).toBe false - - pendingEditor = atom.workspace.buildTextEditor(pending: true) - expect(pendingEditor.isPending()).toBe true - editor3 = TextEditor.deserialize(pendingEditor.serialize(), atom) - expect(editor3.isPending()).toBe true - describe "when the editor is constructed with the largeFileMode option set to true", -> it "loads the editor but doesn't tokenize", -> editor = null @@ -5827,53 +5817,3 @@ describe "TextEditor", -> screenRange: marker1.getRange(), rangeIsReversed: false } - - describe "pending state", -> - editor1 = null - eventCount = null - - beforeEach -> - waitsForPromise -> - atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o - - runs -> - eventCount = 0 - editor1.onDidTerminatePendingState -> eventCount++ - - it "does not open file in pending state by default", -> - expect(editor.isPending()).toBe false - - it "opens file in pending state if 'pending' option is true", -> - expect(editor1.isPending()).toBe true - - it "terminates pending state if ::terminatePendingState is invoked", -> - editor1.terminatePendingState() - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "terminates pending state when buffer is changed", -> - editor1.insertText('I\'ll be back!') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "only calls terminate handler once when text is modified twice", -> - editor1.insertText('Some text') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - editor1.save() - - editor1.insertText('More text') - advanceClock(editor1.getBuffer().stoppedChangingDelay) - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 - - it "only calls terminate handler once when terminatePendingState is called twice", -> - editor1.terminatePendingState() - editor1.terminatePendingState() - - expect(editor1.isPending()).toBe false - expect(eventCount).toBe 1 diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 53889e4c9..c4d028856 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -111,7 +111,7 @@ class TextEditor extends Model @cursors = [] @cursorsByMarkerId = new Map @selections = [] - @bufferHasChanged = false + @hasTerminatedPendingState = false buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ @@ -152,7 +152,6 @@ class TextEditor extends Model firstVisibleScreenColumn: @getFirstVisibleScreenColumn() displayBuffer: @displayBuffer.serialize() selectionsMarkerLayerId: @selectionsMarkerLayer.id - bufferHasChanged: @bufferHasChanged subscribeToBuffer: -> @buffer.retain() @@ -165,10 +164,14 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - @emitter.emit 'did-terminate-pending-state' + @terminatePendingState() if @buffer.isModified() @preserveCursorPositionOnBufferReload() + terminatePendingState: -> + @emitter.emit 'did-terminate-pending-state' if not @hasTerminatedPendingState + @hasTerminatedPendingState = true + onDidTerminatePendingState: (callback) -> @emitter.on 'did-terminate-pending-state', callback From 3848da4488c5a5a00eb679fb49a3faa5bdcefeea Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:21:01 -0800 Subject: [PATCH 044/198] :lipstick: and :memo: for pending API --- src/pane.coffee | 45 ++++++++++---------------------------------- src/workspace.coffee | 5 ++++- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 16c2f79b2..5952352d1 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -58,7 +58,7 @@ class Pane extends Model items: compact(@items.map((item) -> item.serialize?())) activeItemURI: activeItemURI focused: @focused - flexScale: @flexScale # TODO: is it okay to not serialize pending state? does it need to be restored? + flexScale: @flexScale getParent: -> @parent @@ -348,7 +348,9 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. # - # * `pending` TODO + # * `pending` (optional) {Boolean} indicating that the item should be added + # in a pending state if it does not yet exist in the pane. Existing pending + # items in a pane are replaced with new pending items when they are opened. activateItem: (item, pending=false) -> if item? if @isItemPending(@activeItem) @@ -364,7 +366,9 @@ class Pane extends Model # view. # * `index` (optional) {Number} indicating the index at which to add the item. # If omitted, the item is added after the current active item. - # * `pending` TODO + # * `pending` (optional) {Boolean} indicating that the item should be + # added in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> @@ -391,44 +395,15 @@ class Pane extends Model @setActiveItem(item) unless @getActiveItem()? item - clearPendingItem: => - @setPendingItem(null) - setPendingItem: (item) => - # TODO: figure out events for changing/clearing pending item - @pendingItem = item - @emitter.emit 'did-change-pending-item', @pendingItem + @pendingItem = item if @pendingItem isnt item @emitter.emit 'did-terminate-pending-state' if not item - # Public: Get the pending pane item in this pane, if any. - # - # Returns a pane item or `null`. getPendingItem: => @pendingItem or null - isItemPending: (item) => - @pendingItem is item - - # Invoke the given callback when the value of {::getPendingItem} changes. - # - # * `callback` {Function} to be called with when the pending item changes. - # * `pendingItem` The current pending item, or `null`. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChangePendingItem: (callback) => - @emitter.on 'did-change-pending-item', callback - - # Public: Invoke the given callback with the current and future values of - # {::getPendingItem}. - # - # * `callback` {Function} to be called with the current and future pending - # items. - # * `pendingItem` The current pending item. - # - # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - observePendingItem: (callback) -> - callback(@getPendingItem()) - @onDidChangePendingItem(callback) + clearPendingItem: => + @setPendingItem(null) onDidTerminatePendingState: (callback) => @emitter.on 'did-terminate-pending-state', callback diff --git a/src/workspace.coffee b/src/workspace.coffee index 39ec2c599..636ebfd69 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -403,6 +403,9 @@ class Workspace extends Model # containing pane. Defaults to `true`. # * `activateItem` A {Boolean} indicating whether to call {Pane::activateItem} # on containing pane. Defaults to `true`. + # * `pending` A {Boolean} indicating whether or not the item should be opened + # in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # * `searchAllPanes` A {Boolean}. If `true`, the workspace will attempt to # activate an existing item for the given URI on any pane. # If `false`, only the active pane will be searched for @@ -477,7 +480,7 @@ class Workspace extends Model if uri? if item = pane.itemForURI(uri) - pane.setPendingItem(null) if not options.pending + pane.clearPendingItem() if not options.pending and pane.getPendingItem() is item item ?= opener(uri, options) for opener in @getOpeners() when not item try From 6add9ce9e48a278911a10a52e69b1b367faf878f Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:25:39 -0800 Subject: [PATCH 045/198] isItemPending(item) :arrow_right: getPendingItem() --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 5952352d1..c37486d89 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -353,7 +353,7 @@ class Pane extends Model # items in a pane are replaced with new pending items when they are opened. activateItem: (item, pending=false) -> if item? - if @isItemPending(@activeItem) + if @getPendingItem() is @activeItem index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 From 8fff6b2dd0cc231c470f6dbf9349773298f2b546 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:27:39 -0800 Subject: [PATCH 046/198] isItemPending(item) :arrow_right: getPendingItem() --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index c37486d89..cbcb801c8 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -426,7 +426,7 @@ class Pane extends Model index = @items.indexOf(item) return if index is -1 - @pendingItem = null if @isItemPending(item) + @pendingItem = null if @getPendingItem() is item @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) From 7643fa04ed16eb0d68f001d8049317dab06b9478 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 25 Feb 2016 17:32:17 -0800 Subject: [PATCH 047/198] Small :racehorse: when editing a `TextEditor` that is no longer pending --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index c4d028856..f5aa1f20b 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -164,7 +164,7 @@ class TextEditor extends Model @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() @disposables.add @buffer.onDidChangeModified => - @terminatePendingState() if @buffer.isModified() + @terminatePendingState() if not @hasTerminatedPendingState and @buffer.isModified() @preserveCursorPositionOnBufferReload() From 84a2ef69af1f66ac9f05e718c56ddec7e2ea9db5 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 26 Feb 2016 07:21:18 -0800 Subject: [PATCH 048/198] Fix Workspace#open pending specs --- spec/workspace-spec.coffee | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index ef89636a8..78bbf2fdb 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -588,19 +588,22 @@ describe "Workspace", -> describe "when the file is already open in pending state", -> it "should terminate the pending state", -> editor = null + pane = null waitsForPromise -> - atom.workspace.open('sample.js', pending: true).then (o) -> editor = o - + atom.workspace.open('sample.js', pending: true).then (o) -> + editor = o + pane = atom.workspace.getActivePane() + runs -> - expect(editor.isPending()).toBe true - + expect(pane.getPendingItem()).toEqual editor + waitsForPromise -> - atom.workspace.open('sample.js').then (o) -> editor = o - + atom.workspace.open('sample.js') + runs -> - expect(editor.isPending()).toBe false - + expect(pane.getPendingItem()).toBeNull() + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() @@ -1551,11 +1554,12 @@ describe "Workspace", -> describe "when the core.allowPendingPaneItems option is falsey", -> it "does not open item with `pending: true` option as pending", -> - editor = null + pane = null atom.config.set('core.allowPendingPaneItems', false) waitsForPromise -> - atom.workspace.open('sample.js', pending: true).then (o) -> editor = o + atom.workspace.open('sample.js', pending: true).then -> + pane = atom.workspace.getActivePane() runs -> - expect(editor.isPending()).toBeFalsy() + expect(pane.getPendingItem()).toBeFalsy() From ff0942dc1f7092dfded874527069fbf22debeb9e Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Fri, 26 Feb 2016 07:40:39 -0800 Subject: [PATCH 049/198] :arrow_up: bracket-matcher --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c73fdec8a..5dc94cda9 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.80.0", + "bracket-matcher": "0.80.1", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From 715686fd4e5ff41ee37f2731b820d5dd009c0af9 Mon Sep 17 00:00:00 2001 From: Ian Olsen Date: Fri, 26 Feb 2016 14:11:45 -0800 Subject: [PATCH 050/198] build only my experimental branch on circle --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..a55900cca --- /dev/null +++ b/circle.yml @@ -0,0 +1,4 @@ +general: + branches: + only: + - io-circle-ci From 7a6c8f53a444147a02acd143731f1e9c953aaf9c Mon Sep 17 00:00:00 2001 From: natalieogle Date: Mon, 25 Jan 2016 20:18:23 -0800 Subject: [PATCH 051/198] Add activateMostRecentlyUsedItem to pane model. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- spec/pane-spec.coffee | 21 +++++++++++++++++++++ src/pane.coffee | 28 ++++++++++++++++++++++++++-- src/register-default-commands.coffee | 1 + 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 4859f9b67..3fc2aecf1 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -73,7 +73,7 @@ 'cmd-alt-right': 'pane:show-next-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 9ddb760e2..25bfe3add 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -46,7 +46,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index e4703bac8..bde54c29f 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -52,7 +52,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-next-item' + 'ctrl-tab': 'pane:show-most-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 62971bfd1..9bf2306a9 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,6 +183,27 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] + fdescribe "::activateMostRecentlyUsedItem()", -> + it "sets the active item to the most recently used item", -> + pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) + [item1, item2, item3] = pane.getItems() + pane.itemStack = [] + + pane.activateItem(item3) + expect(pane.getActiveItem()).toBe item3 + pane.activateItem(item1) + expect(pane.getActiveItem()).toBe item1 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateItem(item2) + expect(pane.getActiveItem()).toBe item2 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + expect(pane.itemStack[0]).toBe item1 + pane.destroyItem(item3) + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) diff --git a/src/pane.coffee b/src/pane.coffee index cbcb801c8..a6a47be54 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -44,6 +44,7 @@ class Pane extends Model @emitter = new Emitter @subscriptionsPerItem = new WeakMap @items = [] + @itemStack = [] @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? @@ -285,10 +286,17 @@ class Pane extends Model setActiveItem: (activeItem) -> unless activeItem is @activeItem + @addItemToStack(activeItem) @activeItem = activeItem @emitter.emit 'did-change-active-item', @activeItem @activeItem + # Add item (or move item) to the end of the itemStack + addItemToStack: (newItem) -> + index = @itemStack.indexOf(newItem) + @itemStack.splice(index, 1) unless index is -1 + @itemStack.push(newItem) + # Return an {TextEditor} if the pane item is an {TextEditor}, or null otherwise. getActiveEditor: -> @activeItem if @activeItem instanceof TextEditor @@ -301,6 +309,15 @@ class Pane extends Model itemAtIndex: (index) -> @items[index] + # Makes the most recently used item active. + activateMostRecentlyUsedItem: -> + console.log(@items[0].serialize) + if @items.length > 1 + index = @itemStack.length - 2 + mostRecentlyUsedItem = @itemStack[index] + @itemStack.splice(index, 1) + @setActiveItem(mostRecentlyUsedItem) + # Public: Makes the next item active. activateNextItem: -> index = @getActiveItemIndex() @@ -425,9 +442,8 @@ class Pane extends Model removeItem: (item, moved) -> index = @items.indexOf(item) return if index is -1 - @pendingItem = null if @getPendingItem() is item - + @removeItemFromStack(item) @emitter.emit 'will-remove-item', {item, index, destroyed: not moved, moved} @unsubscribeFromItem(item) @@ -443,6 +459,14 @@ class Pane extends Model @container?.didDestroyPaneItem({item, index, pane: this}) unless moved @destroy() if @items.length is 0 and @config.get('core.destroyEmptyPanes') + # Remove the given item from the itemStack. + # + # * `item` The item to remove. + # * `index` {Number} indicating the index to which to remove the item from the itemStack. + removeItemFromStack: (item) -> + index = @itemStack.indexOf(item) + @itemStack.splice(index, 1) unless index is -1 + # Public: Move the given item to the given index. # # * `item` The item to move. diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 1b1aad2cf..d65f8aced 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -2,6 +2,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', + 'pane:show-most-recently-used-item': -> @getModel().getActivePane().activateMostRecentlyUsedItem() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 6466cb489e349422c0845dbac5b20edf8ef5b14c Mon Sep 17 00:00:00 2001 From: natalieogle Date: Wed, 3 Feb 2016 22:21:46 -0800 Subject: [PATCH 052/198] Add serialize and deserialize functionality to the itemStack. --- spec/pane-spec.coffee | 30 +++++++++++++++++++++++++++++- src/pane.coffee | 16 ++++++++++++++-- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 9bf2306a9..b4057fb4c 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,7 +183,8 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - fdescribe "::activateMostRecentlyUsedItem()", -> + + describe "::activateMostRecentlyUsedItem()", -> it "sets the active item to the most recently used item", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) [item1, item2, item3] = pane.getItems() @@ -915,3 +916,30 @@ describe "Pane", -> pane.focus() newPane = Pane.deserialize(pane.serialize(), atom) expect(newPane.focused).toBe true + + it "can serialize and deserialize the order of the items in the itemStack", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item3, item1, item2] + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.itemStack).toEqual pane.itemStack + expect(newPane.itemStack[2]).toEqual item2 + + it "builds the itemStack if the itemStack is not serialized", -> + [item1, item2, item3] = pane.getItems() + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getItems()).toEqual newPane.itemStack + + it "rebuilds the itemStack if items.length does not match itemStack.length", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item2, item3] + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.getItems()).toEqual newPane.itemStack + + it "does not serialize items in the itemStack if they will not be serialized", -> + [item1, item2, item3] = pane.getItems() + pane.itemStack = [item2, item1, item3] + unserializable = {} + pane.activateItem(unserializable) + + newPane = Pane.deserialize(pane.serialize(), atom) + expect(newPane.itemStack).toEqual [item2, item1, item3] diff --git a/src/pane.coffee b/src/pane.coffee index a6a47be54..0aa1ed8fd 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -20,7 +20,7 @@ class Pane extends Model focused: false @deserialize: (state, {deserializers, applicationDelegate, config, notifications}) -> - {items, activeItemURI, activeItemUri} = state + {items, itemStack, activeItemURI, activeItemUri} = state activeItemURI ?= activeItemUri state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState)) state.activeItem = find state.items, (item) -> @@ -48,15 +48,20 @@ class Pane extends Model @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? + @addItemsToStack(params?.itemStack ? []) @setFlexScale(params?.flexScale ? 1) serialize: -> if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() + itemStack = [] + for item in @items + itemStack.push(@itemStack.indexOf(item)) if typeof item.serialize is 'function' deserializer: 'Pane' id: @id items: compact(@items.map((item) -> item.serialize?())) + itemStack: itemStack activeItemURI: activeItemURI focused: @focused flexScale: @flexScale @@ -291,6 +296,14 @@ class Pane extends Model @emitter.emit 'did-change-active-item', @activeItem @activeItem + # Build the itemStack after deserializing + addItemsToStack: (itemStack) -> + if itemStack.length is 0 or itemStack.length isnt @items.length or itemStack.indexOf(-1) >= 0 + itemStack = @items.map((item) => @items.indexOf(item)) + for item, i in itemStack + index = itemStack.indexOf(i) + @addItemToStack(@items[index]) unless index is -1 + # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> index = @itemStack.indexOf(newItem) @@ -311,7 +324,6 @@ class Pane extends Model # Makes the most recently used item active. activateMostRecentlyUsedItem: -> - console.log(@items[0].serialize) if @items.length > 1 index = @itemStack.length - 2 mostRecentlyUsedItem = @itemStack[index] From fe52ce6011ae780ec362c296630fc3a31bb57ac4 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Mon, 8 Feb 2016 18:58:05 -0800 Subject: [PATCH 053/198] Modify serialize functions and add function to move through the item stack in order of most recently used. --- spec/pane-spec.coffee | 26 +++++++++++++++++++++++ src/pane.coffee | 48 ++++++++++++++++++++++++++----------------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b4057fb4c..d3e8dc535 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -195,16 +195,42 @@ describe "Pane", -> pane.activateItem(item1) expect(pane.getActiveItem()).toBe item1 pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item3 pane.activateItem(item2) expect(pane.getActiveItem()).toBe item2 pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item3 expect(pane.itemStack[0]).toBe item1 pane.destroyItem(item3) pane.activateMostRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item1 + describe "::activateNextRecentlyUsedItem()", -> + it "sets the active item to the next item in the itemStack", -> + pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) + [item1, item2, item3, item4, item5] = pane.getItems() + pane.itemStack = [item3, item1, item2, item5, item4] + + pane.activateItem(item4) + expect(pane.getActiveItem()).toBe item4 + pane.activateMostRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item5 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item2 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item4 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item5 + pane.stopMovingThroughStackAndMoveItemToEndOfStack() + expect(pane.itemStack[4]).toBe item5 + describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) diff --git a/src/pane.coffee b/src/pane.coffee index 0aa1ed8fd..93f399cd0 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -20,7 +20,7 @@ class Pane extends Model focused: false @deserialize: (state, {deserializers, applicationDelegate, config, notifications}) -> - {items, itemStack, activeItemURI, activeItemUri} = state + {items, itemStackIndices, activeItemURI, activeItemUri} = state activeItemURI ?= activeItemUri state.items = compact(items.map (itemState) -> deserializers.deserialize(itemState)) state.activeItem = find state.items, (item) -> @@ -48,20 +48,21 @@ class Pane extends Model @addItems(compact(params?.items ? [])) @setActiveItem(@items[0]) unless @getActiveItem()? - @addItemsToStack(params?.itemStack ? []) + @addItemsToStack(params?.itemStackIndices ? []) @setFlexScale(params?.flexScale ? 1) serialize: -> if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() - itemStack = [] - for item in @items - itemStack.push(@itemStack.indexOf(item)) if typeof item.serialize is 'function' + itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function')) + itemStackIndices = [] + for item in @itemStack + itemStackIndices.push(itemsToBeSerialized.indexOf(item)) if typeof item.serialize is 'function' deserializer: 'Pane' id: @id - items: compact(@items.map((item) -> item.serialize?())) - itemStack: itemStack + items: itemsToBeSerialized.map((item) -> item.serialize()) + itemStackIndices: itemStackIndices activeItemURI: activeItemURI focused: @focused flexScale: @flexScale @@ -289,20 +290,20 @@ class Pane extends Model # Returns a pane item. getActiveItem: -> @activeItem - setActiveItem: (activeItem) -> + setActiveItem: (activeItem, options) -> + {modifyStack} = options if options? unless activeItem is @activeItem - @addItemToStack(activeItem) + @addItemToStack(activeItem) unless modifyStack is false @activeItem = activeItem @emitter.emit 'did-change-active-item', @activeItem @activeItem # Build the itemStack after deserializing - addItemsToStack: (itemStack) -> - if itemStack.length is 0 or itemStack.length isnt @items.length or itemStack.indexOf(-1) >= 0 - itemStack = @items.map((item) => @items.indexOf(item)) - for item, i in itemStack - index = itemStack.indexOf(i) - @addItemToStack(@items[index]) unless index is -1 + addItemsToStack: (itemStackIndices) -> + if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 + itemStackIndices = (i for i in [0..@items.length-1]) + for itemIndex in itemStackIndices + @addItemToStack(@items[itemIndex]) # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> @@ -325,10 +326,19 @@ class Pane extends Model # Makes the most recently used item active. activateMostRecentlyUsedItem: -> if @items.length > 1 - index = @itemStack.length - 2 - mostRecentlyUsedItem = @itemStack[index] - @itemStack.splice(index, 1) - @setActiveItem(mostRecentlyUsedItem) + @itemStackIndex = @itemStack.length - 1 + @activateNextRecentlyUsedItem() + + # Makes the next item in the itemStack active. + activateNextRecentlyUsedItem: -> + @itemStackIndex = @itemStackIndex - 1 + nextRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(nextRecentlyUsedItem, modifyStack: false) + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + + # Moves the active item to the end of the itemStack once the ctrl key is lifted + stopMovingThroughStackAndMoveItemToEndOfStack: -> + @addItemToStack(@activeItem) # Public: Makes the next item active. activateNextItem: -> From 3641cc0296ab4720b57e3538ed6e2ccea702eeec Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 9 Feb 2016 20:43:02 -0800 Subject: [PATCH 054/198] Remove redundant MRU function. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- spec/pane-spec.coffee | 41 ++++++++-------------------- src/pane.coffee | 17 +++++------- src/register-default-commands.coffee | 2 +- 6 files changed, 23 insertions(+), 43 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 3fc2aecf1..f854aaeaf 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -73,7 +73,7 @@ 'cmd-alt-right': 'pane:show-next-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 25bfe3add..433975e89 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -46,7 +46,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index bde54c29f..fb33ce09d 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -52,7 +52,7 @@ 'pagedown': 'core:page-down' 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' - 'ctrl-tab': 'pane:show-most-recently-used-item' + 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d3e8dc535..f17fee5b1 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,31 +183,6 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - - describe "::activateMostRecentlyUsedItem()", -> - it "sets the active item to the most recently used item", -> - pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) - [item1, item2, item3] = pane.getItems() - pane.itemStack = [] - - pane.activateItem(item3) - expect(pane.getActiveItem()).toBe item3 - pane.activateItem(item1) - expect(pane.getActiveItem()).toBe item1 - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item3 - pane.activateItem(item2) - expect(pane.getActiveItem()).toBe item2 - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item3 - expect(pane.itemStack[0]).toBe item1 - pane.destroyItem(item3) - pane.activateMostRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.getActiveItem()).toBe item1 - describe "::activateNextRecentlyUsedItem()", -> it "sets the active item to the next item in the itemStack", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) @@ -216,20 +191,28 @@ describe "Pane", -> pane.activateItem(item4) expect(pane.getActiveItem()).toBe item4 - pane.activateMostRecentlyUsedItem() + pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() + pane.stopMovingThroughStackAndMoveItemToEndOfStack() expect(pane.getActiveItem()).toBe item1 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item3 + expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item4 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item2 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activateNextRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 + pane.activateNextRecentlyUsedItem() pane.stopMovingThroughStackAndMoveItemToEndOfStack() - expect(pane.itemStack[4]).toBe item5 + expect(pane.getActiveItem()).toBe item4 + expect(pane.itemStack[4]).toBe item4 describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> diff --git a/src/pane.coffee b/src/pane.coffee index 93f399cd0..82b7ec4f6 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -323,21 +323,18 @@ class Pane extends Model itemAtIndex: (index) -> @items[index] - # Makes the most recently used item active. - activateMostRecentlyUsedItem: -> - if @items.length > 1 - @itemStackIndex = @itemStack.length - 1 - @activateNextRecentlyUsedItem() - # Makes the next item in the itemStack active. activateNextRecentlyUsedItem: -> - @itemStackIndex = @itemStackIndex - 1 - nextRecentlyUsedItem = @itemStack[@itemStackIndex] - @setActiveItem(nextRecentlyUsedItem, modifyStack: false) - @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + if @items.length > 1 + @itemStackIndex = @itemStack.length - 1 unless @itemStackIndex? + @itemStackIndex = @itemStackIndex - 1 + nextRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(nextRecentlyUsedItem, modifyStack: false) + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted stopMovingThroughStackAndMoveItemToEndOfStack: -> + delete @itemStackIndex @addItemToStack(@activeItem) # Public: Makes the next item active. diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index d65f8aced..ae5cdc344 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -2,7 +2,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', - 'pane:show-most-recently-used-item': -> @getModel().getActivePane().activateMostRecentlyUsedItem() + 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 69a6b9e5c5c004f0df085ec8a161107b08577218 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 16 Feb 2016 20:28:58 -0800 Subject: [PATCH 055/198] Add keymap for 'ctrl-tab ^ctrl' in order to move item to top of stack when lifting ctrl. --- keymaps/darwin.cson | 1 + keymaps/linux.cson | 1 + keymaps/win32.cson | 1 + src/pane.coffee | 2 +- src/register-default-commands.coffee | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index f854aaeaf..c39ee6d99 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -74,6 +74,7 @@ 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 433975e89..1192fe052 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -47,6 +47,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index fb33ce09d..c50a6e75a 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -53,6 +53,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' + 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/src/pane.coffee b/src/pane.coffee index 82b7ec4f6..277835d3c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -333,7 +333,7 @@ class Pane extends Model @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted - stopMovingThroughStackAndMoveItemToEndOfStack: -> + moveItemToTopOfStack: -> delete @itemStackIndex @addItemToStack(@activeItem) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index ae5cdc344..09574cedb 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,6 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() + 'pane:move-item-to-top-of-stack': -> @getModel().getActivePane().moveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From 463dc6955a638c943c5c67124bcdabef402df777 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 16 Feb 2016 21:12:05 -0800 Subject: [PATCH 056/198] Update spec for MRU tab functionality with correct function name. --- spec/pane-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index f17fee5b1..c9d467af3 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -196,7 +196,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() + pane.moveItemToTopOfStack() expect(pane.getActiveItem()).toBe item1 expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() @@ -210,7 +210,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() - pane.stopMovingThroughStackAndMoveItemToEndOfStack() + pane.moveItemToTopOfStack() expect(pane.getActiveItem()).toBe item4 expect(pane.itemStack[4]).toBe item4 From 96107038746354f77494bce1b3317ea8936eb436 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sun, 21 Feb 2016 18:50:54 -0800 Subject: [PATCH 057/198] Add check to only build itemStack if there are items. --- src/pane.coffee | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 277835d3c..1504553af 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -300,10 +300,11 @@ class Pane extends Model # Build the itemStack after deserializing addItemsToStack: (itemStackIndices) -> - if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 - itemStackIndices = (i for i in [0..@items.length-1]) - for itemIndex in itemStackIndices - @addItemToStack(@items[itemIndex]) + if @items.length > 0 + if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 + itemStackIndices = (i for i in [0..@items.length-1]) unless @items.length is 0 + for itemIndex in itemStackIndices + @addItemToStack(@items[itemIndex]) # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> From 48ef6725241ca69d40f0f439f3ed7d4b0e1230b5 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sun, 21 Feb 2016 19:11:32 -0800 Subject: [PATCH 058/198] Remove redundant items.length check. --- src/pane.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane.coffee b/src/pane.coffee index 1504553af..7b3ec515b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -302,7 +302,7 @@ class Pane extends Model addItemsToStack: (itemStackIndices) -> if @items.length > 0 if itemStackIndices.length is 0 or itemStackIndices.length isnt @items.length or itemStackIndices.indexOf(-1) >= 0 - itemStackIndices = (i for i in [0..@items.length-1]) unless @items.length is 0 + itemStackIndices = (i for i in [0..@items.length-1]) for itemIndex in itemStackIndices @addItemToStack(@items[itemIndex]) From 553b3f33009850b3e413f57aec26b4657406e61d Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 19:02:45 -0800 Subject: [PATCH 059/198] Change name of function that moves the active item to the top of the item stack. --- keymaps/darwin.cson | 2 +- keymaps/linux.cson | 2 +- keymaps/win32.cson | 2 +- src/pane.coffee | 2 +- src/register-default-commands.coffee | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index c39ee6d99..baa664da8 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -74,7 +74,7 @@ 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 1192fe052..d9758e5ea 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -47,7 +47,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index c50a6e75a..3befc6b0c 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -53,7 +53,7 @@ 'backspace': 'core:backspace' 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' - 'ctrl-tab ^ctrl': 'pane:move-item-to-top-of-stack' + 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-shift-tab': 'pane:show-previous-item' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' diff --git a/src/pane.coffee b/src/pane.coffee index 7b3ec515b..d405e77a7 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -334,7 +334,7 @@ class Pane extends Model @itemStackIndex = @itemStack.length if @itemStackIndex is 0 # Moves the active item to the end of the itemStack once the ctrl key is lifted - moveItemToTopOfStack: -> + moveActiveItemToTopOfStack: -> delete @itemStackIndex @addItemToStack(@activeItem) diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 09574cedb..3d95f1cff 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,7 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() - 'pane:move-item-to-top-of-stack': -> @getModel().getActivePane().moveItemToTopOfStack() + 'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() 'pane:show-item-1': -> @getModel().getActivePane().activateItemAtIndex(0) From bc28a91e02f9248e530168fa39a14c5ab6b6ecfe Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 19:20:18 -0800 Subject: [PATCH 060/198] :art: Change the structure of a few pieces relating to serialization. --- src/pane.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index d405e77a7..f2e92480b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -55,9 +55,7 @@ class Pane extends Model if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() itemsToBeSerialized = compact(@items.map((item) -> item if typeof item.serialize is 'function')) - itemStackIndices = [] - for item in @itemStack - itemStackIndices.push(itemsToBeSerialized.indexOf(item)) if typeof item.serialize is 'function' + itemStackIndices = (itemsToBeSerialized.indexOf(item) for item in @itemStack when typeof item.serialize is 'function') deserializer: 'Pane' id: @id @@ -305,6 +303,7 @@ class Pane extends Model itemStackIndices = (i for i in [0..@items.length-1]) for itemIndex in itemStackIndices @addItemToStack(@items[itemIndex]) + return # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> From f7de9052d6bef9fadb77c594f464db45c8ce9f7f Mon Sep 17 00:00:00 2001 From: natalieogle Date: Tue, 23 Feb 2016 20:04:16 -0800 Subject: [PATCH 061/198] Update specs for itemStack. --- spec/pane-spec.coffee | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index c9d467af3..d4e09e645 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -196,7 +196,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 pane.activateNextRecentlyUsedItem() - pane.moveItemToTopOfStack() + pane.moveActiveItemToTopOfStack() expect(pane.getActiveItem()).toBe item1 expect(pane.itemStack[4]).toBe item1 pane.activateNextRecentlyUsedItem() @@ -210,7 +210,7 @@ describe "Pane", -> pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() - pane.moveItemToTopOfStack() + pane.moveActiveItemToTopOfStack() expect(pane.getActiveItem()).toBe item4 expect(pane.itemStack[4]).toBe item4 @@ -280,7 +280,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")])) [item1, item2, item3] = pane.getItems() - it "removes the item from the items list and destroyes it", -> + it "removes the item from the items list and destroys it", -> expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item2) expect(item2 in pane.getItems()).toBe false @@ -291,6 +291,19 @@ describe "Pane", -> expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true + it "removes the item from the itemStack", -> + pane.itemStack = [item2, item3, item1] + + pane.activateItem(item1) + expect(pane.getActiveItem()).toBe item1 + pane.destroyItem(item3) + expect(pane.itemStack).toEqual [item2, item1] + expect(pane.getActiveItem()).toBe item1 + + pane.destroyItem(item1) + expect(pane.itemStack).toEqual [item2] + expect(pane.getActiveItem()).toBe item2 + it "invokes ::onWillDestroyItem() observers before destroying the item", -> events = [] pane.onWillDestroyItem (event) -> @@ -944,7 +957,7 @@ describe "Pane", -> newPane = Pane.deserialize(pane.serialize(), atom) expect(newPane.getItems()).toEqual newPane.itemStack - it "does not serialize items in the itemStack if they will not be serialized", -> + it "does not serialize the reference to the items in the itemStack for pane items that will not be serialized", -> [item1, item2, item3] = pane.getItems() pane.itemStack = [item2, item1, item3] unserializable = {} From 420a8d8692080a1d067caa936f6888ff6653a2f7 Mon Sep 17 00:00:00 2001 From: natalieogle Date: Sat, 27 Feb 2016 20:41:25 -0800 Subject: [PATCH 062/198] Add activatePreviousRecentlyUsedItem to pane model and add specs. --- keymaps/darwin.cson | 3 ++- keymaps/linux.cson | 3 ++- keymaps/win32.cson | 3 ++- spec/pane-spec.coffee | 26 ++++++++++++-------------- src/pane.coffee | 11 ++++++++++- src/register-default-commands.coffee | 1 + 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index baa664da8..819e0079e 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -75,7 +75,8 @@ 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' 'cmd--': 'window:decrease-font-size' diff --git a/keymaps/linux.cson b/keymaps/linux.cson index d9758e5ea..e676ed5ab 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -48,7 +48,8 @@ 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-up': 'core:move-up' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 3befc6b0c..5869a3ed8 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -54,7 +54,8 @@ 'shift-backspace': 'core:backspace' 'ctrl-tab': 'pane:show-next-recently-used-item' 'ctrl-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' - 'ctrl-shift-tab': 'pane:show-previous-item' + 'ctrl-shift-tab': 'pane:show-previous-recently-used-item' + 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'ctrl-pageup': 'pane:show-previous-item' 'ctrl-pagedown': 'pane:show-next-item' 'ctrl-shift-up': 'core:move-up' diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index d4e09e645..e5cbee1a6 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,8 +183,8 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] - describe "::activateNextRecentlyUsedItem()", -> - it "sets the active item to the next item in the itemStack", -> + describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", -> + it "sets the active item to the next/previous item in the itemStack, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) [item1, item2, item3, item4, item5] = pane.getItems() pane.itemStack = [item3, item1, item2, item5, item4] @@ -195,24 +195,22 @@ describe "Pane", -> expect(pane.getActiveItem()).toBe item5 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item2 - pane.activateNextRecentlyUsedItem() - pane.moveActiveItemToTopOfStack() - expect(pane.getActiveItem()).toBe item1 - expect(pane.itemStack[4]).toBe item1 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item4 - pane.activateNextRecentlyUsedItem() + pane.activatePreviousRecentlyUsedItem() expect(pane.getActiveItem()).toBe item5 - pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item2 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item4 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item3 + pane.activatePreviousRecentlyUsedItem() + expect(pane.getActiveItem()).toBe item1 pane.activateNextRecentlyUsedItem() expect(pane.getActiveItem()).toBe item3 pane.activateNextRecentlyUsedItem() - expect(pane.getActiveItem()).toBe item1 + expect(pane.getActiveItem()).toBe item4 pane.activateNextRecentlyUsedItem() pane.moveActiveItemToTopOfStack() - expect(pane.getActiveItem()).toBe item4 - expect(pane.itemStack[4]).toBe item4 + expect(pane.getActiveItem()).toBe item5 + expect(pane.itemStack[4]).toBe item5 describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> diff --git a/src/pane.coffee b/src/pane.coffee index f2e92480b..12abc5448 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -327,10 +327,19 @@ class Pane extends Model activateNextRecentlyUsedItem: -> if @items.length > 1 @itemStackIndex = @itemStack.length - 1 unless @itemStackIndex? + @itemStackIndex = @itemStack.length if @itemStackIndex is 0 @itemStackIndex = @itemStackIndex - 1 nextRecentlyUsedItem = @itemStack[@itemStackIndex] @setActiveItem(nextRecentlyUsedItem, modifyStack: false) - @itemStackIndex = @itemStack.length if @itemStackIndex is 0 + + # Makes the previous item in the itemStack active. + activatePreviousRecentlyUsedItem: -> + if @items.length > 1 + if @itemStackIndex + 1 is @itemStack.length or not @itemStackIndex? + @itemStackIndex = -1 + @itemStackIndex = @itemStackIndex + 1 + previousRecentlyUsedItem = @itemStack[@itemStackIndex] + @setActiveItem(previousRecentlyUsedItem, modifyStack: false) # Moves the active item to the end of the itemStack once the ctrl key is lifted moveActiveItemToTopOfStack: -> diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 3d95f1cff..dacb1d228 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -3,6 +3,7 @@ module.exports = ({commandRegistry, commandInstaller, config}) -> commandRegistry.add 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() + 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() 'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack() 'pane:show-next-item': -> @getModel().getActivePane().activateNextItem() 'pane:show-previous-item': -> @getModel().getActivePane().activatePreviousItem() From 5dfd0c9102e683b00212bbfea89d775ea6b7c457 Mon Sep 17 00:00:00 2001 From: Johnston Jiaa Date: Sun, 28 Feb 2016 11:48:27 -0500 Subject: [PATCH 063/198] Show tooltip immediately if the tooltip trigger is manual --- spec/tooltip-manager-spec.coffee | 4 ++++ src/tooltip-manager.coffee | 2 ++ src/tooltip.js | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee index 87082504a..264d1a3bb 100644 --- a/spec/tooltip-manager-spec.coffee +++ b/spec/tooltip-manager-spec.coffee @@ -28,6 +28,10 @@ describe "TooltipManager", -> hover element, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") + it "creates a tooltip immediately if the trigger type is manual", -> + manager.add element, title: "Title", trigger: "manual" + expect(document.body.querySelector(".tooltip")).toHaveText("Title") + it "allows jQuery elements to be passed as the target", -> element2 = document.createElement('div') jasmine.attachToDOM(element2) diff --git a/src/tooltip-manager.coffee b/src/tooltip-manager.coffee index 247437535..90f0ab8e6 100644 --- a/src/tooltip-manager.coffee +++ b/src/tooltip-manager.coffee @@ -63,6 +63,8 @@ class TooltipManager # full list of options. You can also supply the following additional options: # * `title` A {String} or {Function} to use for the text in the tip. If # given a function, `this` will be set to the `target` element. + # * `trigger` A {String} that's the same as Bootstrap 'click | hover | focus + # | manual', except 'manual' will show the tooltip immediately. # * `keyBindingCommand` A {String} containing a command name. If you specify # this option and a key binding exists that matches the command, it will # be appended to the title or rendered alone if no title is specified. diff --git a/src/tooltip.js b/src/tooltip.js index 4ea952a64..ad5ce0cdd 100644 --- a/src/tooltip.js +++ b/src/tooltip.js @@ -64,7 +64,9 @@ Tooltip.prototype.init = function (element, options) { if (trigger === 'click') { this.disposables.add(listen(this.element, 'click', this.options.selector, this.toggle.bind(this))) - } else if (trigger !== 'manual') { + } else if (trigger === 'manual') { + this.show() + } else { var eventIn, eventOut if (trigger === 'hover') { From 92bbbf064508d50f75b84e4e2195f194dfd9facc Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Sun, 28 Feb 2016 15:05:07 -0800 Subject: [PATCH 064/198] Windows command line does not allow ELSE on separate line, fixes #10934 --- resources/win/atom.cmd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index c9bfdd5ba..8d5e6f97a 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -35,8 +35,7 @@ IF "%EXPECT_OUTPUT%"=="YES" ( "%~dp0\..\..\atom.exe" --pid=%PID% %* rem If the wait flag is set, don't exit this process until Atom tells it to. goto waitLoop - ) - ELSE ( + ) ELSE ( "%~dp0\..\..\atom.exe" %* ) ) ELSE ( From 2dad38a7826522455feb56bd33f276f6b8eeeb49 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 17:57:59 -0800 Subject: [PATCH 065/198] onDidTerminatePendingState :arrow_right: onItemDidTerminatePendingState Signed-off-by: Katrina Uychaco --- src/pane.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 12abc5448..9905680bd 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -441,8 +441,10 @@ class Pane extends Model item setPendingItem: (item) => - @pendingItem = item if @pendingItem isnt item - @emitter.emit 'did-terminate-pending-state' if not item + if @pendingItem isnt item + mostRecentPendingItem = @pendingItem + @pendingItem = item + @emitter.emit 'item-did-terminate-pending-state', mostRecentPendingItem getPendingItem: => @pendingItem or null @@ -450,8 +452,8 @@ class Pane extends Model clearPendingItem: => @setPendingItem(null) - onDidTerminatePendingState: (callback) => - @emitter.on 'did-terminate-pending-state', callback + onItemDidTerminatePendingState: (callback) => + @emitter.on 'item-did-terminate-pending-state', callback # Public: Add the given items to the pane. # From 92dea0379a041d0c43f37df8c8068485aef02eb6 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 18:32:19 -0800 Subject: [PATCH 066/198] :arrow_up: tree-view Signed-off-by: Katrina Uychaco --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dc94cda9..8830a3f98 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.90.2", "timecop": "0.33.1", - "tree-view": "0.201.4", + "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From d39418ad7c61626753ffc9ed4536341ef0fc0b12 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 28 Feb 2016 18:15:52 -0800 Subject: [PATCH 067/198] Add specs for Pane::setPendingItem and ::onItemDidTerminatePendingState Signed-off-by: Katrina Uychaco --- spec/pane-spec.coffee | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index e5cbee1a6..b4785e113 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -183,6 +183,41 @@ describe "Pane", -> pane.activateItem(itemD, true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] + describe "::setPendingItem", -> + pane = null + + beforeEach -> + pane = atom.workspace.getActivePane() + + it "changes the pending item", -> + expect(pane.getPendingItem()).toBeNull() + pane.setPendingItem("fake item") + expect(pane.getPendingItem()).toEqual "fake item" + + describe "::onItemDidTerminatePendingState callback", -> + pane = null + callbackCalled = false + + beforeEach -> + pane = atom.workspace.getActivePane() + callbackCalled = false + + it "is called when the pending item changes", -> + pane.setPendingItem("fake item one") + pane.onItemDidTerminatePendingState (item) -> + callbackCalled = true + expect(item).toEqual "fake item one" + pane.setPendingItem("fake item two") + expect(callbackCalled).toBeTruthy() + + it "has access to the new pending item via ::getPendingItem", -> + pane.setPendingItem("fake item one") + pane.onItemDidTerminatePendingState (item) -> + callbackCalled = true + expect(pane.getPendingItem()).toEqual "fake item two" + pane.setPendingItem("fake item two") + expect(callbackCalled).toBeTruthy() + describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", -> it "sets the active item to the next/previous item in the itemStack, looping around at either end", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")])) From 4769e601bf9e3e0595789a276a29f0cd2e1e5b5a Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 29 Feb 2016 11:27:38 -0800 Subject: [PATCH 068/198] Move spec from tabs package See https://github.com/atom/tabs/pull/274#issuecomment-190064071 Signed-off-by: Katrina Uychaco --- spec/workspace-spec.coffee | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 78bbf2fdb..2e15431b2 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -604,6 +604,25 @@ describe "Workspace", -> runs -> expect(pane.getPendingItem()).toBeNull() + describe "when opening will switch from a pending tab to a permanent tab", -> + it "keeps the pending tab open", -> + editor1 = null + editor2 = null + + waitsForPromise -> + atom.workspace.open('sample.txt').then (o) -> + editor1 = o + + waitsForPromise -> + atom.workspace.open('sample2.txt', pending: true).then (o) -> + editor2 = o + + runs -> + pane = atom.workspace.getActivePane() + pane.activateItem(editor1) + expect(pane.getItems().length).toBe 2 + expect(pane.getItems()).toEqual [editor1, editor2] + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() From 920d348014ac2ddf42e54e0bed6bb2cd71465146 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 11:32:28 -0800 Subject: [PATCH 069/198] Only move legit items to the top of the stack Fixes #11002 cc @natalieogle --- spec/pane-spec.coffee | 4 ++++ src/pane.coffee | 1 + 2 files changed, 5 insertions(+) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index b4785e113..888ccf6e2 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -337,6 +337,10 @@ describe "Pane", -> expect(pane.itemStack).toEqual [item2] expect(pane.getActiveItem()).toBe item2 + pane.destroyItem(item2) + expect(pane.itemStack).toEqual [] + expect(pane.getActiveItem()).toBeUndefined() + it "invokes ::onWillDestroyItem() observers before destroying the item", -> events = [] pane.onWillDestroyItem (event) -> diff --git a/src/pane.coffee b/src/pane.coffee index 9905680bd..25f421934 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -307,6 +307,7 @@ class Pane extends Model # Add item (or move item) to the end of the itemStack addItemToStack: (newItem) -> + return unless newItem? index = @itemStack.indexOf(newItem) @itemStack.splice(index, 1) unless index is -1 @itemStack.push(newItem) From c849216084d248402c3d9a81fe7d55cacc0b7545 Mon Sep 17 00:00:00 2001 From: Jonathan Willis Date: Mon, 29 Feb 2016 14:49:19 -0500 Subject: [PATCH 070/198] Config: Added documentation for order key in config. --- src/config.coffee | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/config.coffee b/src/config.coffee index 348b7b94f..66f07516e 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -319,6 +319,23 @@ ScopeDescriptor = require './scope-descriptor' # * line breaks - `line breaks
` # * ~~strikethrough~~ - `~~strikethrough~~` # +# #### order +# +# The settings view orders your settings alphabetically. You can override this +# ordering with the order key. +# +# ```coffee +# config: +# zSetting: +# type: 'integer' +# default: 4 +# order: 1 +# aSetting: +# type: 'integer' +# default: 4 +# order: 2 +# ``` +# # ## Best practices # # * Don't depend on (or write to) configuration keys outside of your keypath. From c99260bce75b852f60581782630813ccd5ab4e39 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 29 Feb 2016 15:04:36 -0500 Subject: [PATCH 071/198] Disable EOL diffs for diff stats too. --- src/git-repository-async.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 894b1216a..91b78e0c5 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -596,7 +596,15 @@ export default class GitRepositoryAsync { .then(([repo, headCommit]) => Promise.all([repo, headCommit.getTree()])) .then(([repo, tree]) => { const options = new Git.DiffOptions() + options.contextLines = 0 + options.flags = Git.Diff.OPTION.DISABLE_PATHSPEC_MATCH options.pathspec = this.relativize(_path, repo.workdir()) + if (process.platform === 'win32') { + // Ignore eol of line differences on windows so that files checked in + // as LF don't report every line modified when the text contains CRLF + // endings. + options.flags |= Git.Diff.OPTION.IGNORE_WHITESPACE_EOL + } return Git.Diff.treeToWorkdir(repo, tree, options) }) .then(diff => this._getDiffLines(diff)) From 9a938064cb72ecce7ebed2a795fd53727591a3f0 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Mon, 29 Feb 2016 14:09:01 -0800 Subject: [PATCH 072/198] :arrow_up: tabs@0.91.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8830a3f98..42cf22f8b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.90.2", + "tabs": "0.91.0", "timecop": "0.33.1", "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", From 26cf7f081fe84ad8d08000f4b605d6dbd11c5906 Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Fri, 26 Feb 2016 16:20:39 -0500 Subject: [PATCH 073/198] Registry for editors --- spec/text-editor-registry-spec.coffee | 38 +++++++++++++++++++++++++++ src/atom-environment.coffee | 6 +++++ src/text-editor-registry.coffee | 33 +++++++++++++++++++++++ src/text-editor.coffee | 5 +++- src/workspace.coffee | 5 +++- 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 spec/text-editor-registry-spec.coffee create mode 100644 src/text-editor-registry.coffee diff --git a/spec/text-editor-registry-spec.coffee b/spec/text-editor-registry-spec.coffee new file mode 100644 index 000000000..04665bef2 --- /dev/null +++ b/spec/text-editor-registry-spec.coffee @@ -0,0 +1,38 @@ +TextEditorRegistry = require '../src/text-editor-registry' + +describe "TextEditorRegistry", -> + [registry, editor] = [] + + beforeEach -> + registry = new TextEditorRegistry + + describe "when a TextEditor is added", -> + it "gets added to the list of registered editors", -> + editor = {} + registry.add(editor) + expect(registry.editors.size).toBe 1 + expect(registry.editors.has(editor)).toBe(true) + + it "returns a Disposable that can unregister the editor", -> + editor = {} + disposable = registry.add(editor) + expect(registry.editors.size).toBe 1 + disposable.dispose() + expect(registry.editors.size).toBe 0 + + describe "when the registry is observed", -> + it "calls the callback for current and future editors until unsubscribed", -> + [editor1, editor2, editor3] = [{}, {}, {}] + + registry.add(editor1) + subscription = registry.observe spy = jasmine.createSpy() + expect(spy.calls.length).toBe 1 + + registry.add(editor2) + expect(spy.calls.length).toBe 2 + expect(spy.argsForCall[0][0]).toBe editor1 + expect(spy.argsForCall[1][0]).toBe editor2 + + subscription.dispose() + registry.add(editor3) + expect(spy.calls.length).toBe 2 diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index c42bf05aa..0ee12fe93 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -40,6 +40,7 @@ Project = require './project' TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' +TextEditorRegistry = require './text-editor-registry' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -111,6 +112,9 @@ class AtomEnvironment extends Model # Public: A {Workspace} instance workspace: null + # Public: A {TextEditorRegistry} instance + textEditors: null + saveStateDebounceInterval: 1000 ### @@ -183,6 +187,8 @@ class AtomEnvironment extends Model }) @themes.workspace = @workspace + @textEditors = new TextEditorRegistry + @config.load() @themes.loadBaseStylesheets() diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee new file mode 100644 index 000000000..fc95f9cd7 --- /dev/null +++ b/src/text-editor-registry.coffee @@ -0,0 +1,33 @@ +{Emitter, Disposable} = require 'event-kit' + +# This global registry tracks registered `TextEditors`. +# +# Packages that provide extra functionality to `TextEditors`, such as +# autocompletion, can observe this registry to find applicable editors. +module.exports = +class TextEditorRegistry + constructor: -> + @editors = new Set + @emitter = new Emitter + + # Register a `TextEditor`. + # + # * `editor` The editor to register. + # + # Returns a {Disposable} on which `.dispose()` can be called to remove the + # added editor. To avoid any memory leaks this should be called when the + # editor is destroyed. + add: (editor) -> + @editors.add(editor) + @emitter.emit 'did-add-editor', editor + new Disposable => @editors.delete(editor) + + # Invoke the given callback with all the current and future registered + # `TextEditors`. + # + # * `callback` {Function} to be called with current and future text editors. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observe: (callback) -> + @editors.forEach (editor) -> callback(editor) + @emitter.on 'did-add-editor', callback diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5d55e0e48..d7fae17b3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -82,7 +82,10 @@ class TextEditor extends Model state.project = atomEnvironment.project state.assert = atomEnvironment.assert.bind(atomEnvironment) state.applicationDelegate = atomEnvironment.applicationDelegate - new this(state) + editor = new this(state) + disposable = atomEnvironment.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() + editor constructor: (params={}) -> super diff --git a/src/workspace.coffee b/src/workspace.coffee index 636ebfd69..c925a495a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -558,7 +558,10 @@ class Workspace extends Model @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, @project, @assert, @applicationDelegate }, params) - new TextEditor(params) + editor = new TextEditor(params) + disposable = atom.textEditors.add(editor) + editor.onDidDestroy -> disposable.dispose() + editor # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. From 8959e414a63aea30f9fc3f7b776a4444caf93212 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 29 Feb 2016 15:54:47 -0800 Subject: [PATCH 074/198] :arrow_up: tabs Signed-off-by: Katrina Uychaco --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42cf22f8b..e6e63d31b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.91.0", + "tabs": "0.91.1", "timecop": "0.33.1", "tree-view": "0.201.5", "update-package-dependencies": "0.10.0", From 98516150788b9b5022019179ce0bf1b5c8db3448 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 29 Feb 2016 16:24:22 -0800 Subject: [PATCH 075/198] :arrow_up: find-and-replace Signed-off-by: Michelle Tilley --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6e63d31b..9ba72dc83 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.197.2", + "find-and-replace": "0.197.3", "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", From 6292484c971827c2137b3ce2fc02e33979cd664a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:30:03 -0700 Subject: [PATCH 076/198] Always strip git+ prefix and .git suffix from package repository URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, a guard based on the presence of the _id field (which is inserted by npm during installation) prevented a regex replacement of the git+ prefix on URLs. Now we always do this. Since the .git suffix also causes problems and we’re removing that in packages, I now remove that as well. --- .../package.json | 8 ++++++++ spec/package-manager-spec.coffee | 7 ++++++- src/package-manager.coffee | 5 +++-- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json diff --git a/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json b/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json new file mode 100644 index 000000000..ce57f7501 --- /dev/null +++ b/spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json @@ -0,0 +1,8 @@ +{ + "name": "package-with-a-git-prefixed-git-repo-url", + "repository": { + "type": "git", + "url": "git+https://github.com/example/repo.git" + }, + "_id": "this is here to simulate the URL being already normalized by npm. we still need to stript git+ from the beginning and .git from the end." +} diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 0a2d614b3..3b54691b2 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -55,12 +55,17 @@ describe "PackageManager", -> it "normalizes short repository urls in package.json", -> {metadata} = atom.packages.loadPackage("package-with-short-url-package-json") expect(metadata.repository.type).toBe "git" - expect(metadata.repository.url).toBe "https://github.com/example/repo.git" + expect(metadata.repository.url).toBe "https://github.com/example/repo" {metadata} = atom.packages.loadPackage("package-with-invalid-url-package-json") expect(metadata.repository.type).toBe "git" expect(metadata.repository.url).toBe "foo" + it "trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ", -> + {metadata} = atom.packages.loadPackage("package-with-prefixed-and-suffixed-repo-url") + expect(metadata.repository.type).toBe "git" + expect(metadata.repository.url).toBe "https://github.com/example/repo" + it "returns null if the package is not found in any package directory", -> spyOn(console, 'warn') expect(atom.packages.loadPackage("this-package-cannot-be-found")).toBeNull() diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 33f8f86a3..94b55a793 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -541,11 +541,12 @@ class PackageManager unless typeof metadata.name is 'string' and metadata.name.length > 0 metadata.name = packageName + if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string' + metadata.repository.url = metadata.repository.url.replace(/(^git\+)|(\.git$)/g, '') + metadata normalizePackageMetadata: (metadata) -> unless metadata?._id normalizePackageData ?= require 'normalize-package-data' normalizePackageData(metadata) - if metadata.repository?.type is 'git' and typeof metadata.repository.url is 'string' - metadata.repository.url = metadata.repository.url.replace(/^git\+/, '') From b95fd26cce2ced1a5b4d28f92bde8fd3a1a28f88 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:38:47 -0700 Subject: [PATCH 077/198] Adjust TextEditorRegistry docs /cc @wvanlint --- src/text-editor-registry.coffee | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee index fc95f9cd7..943a0d31a 100644 --- a/src/text-editor-registry.coffee +++ b/src/text-editor-registry.coffee @@ -1,9 +1,16 @@ {Emitter, Disposable} = require 'event-kit' -# This global registry tracks registered `TextEditors`. +# Experimental: This global registry tracks registered `TextEditors`. # -# Packages that provide extra functionality to `TextEditors`, such as -# autocompletion, can observe this registry to find applicable editors. +# If you want to add functionality to a wider set of text editors than just +# those appearing within workspace panes, use `atom.textEditors.observe` to +# invoke a callback for all current and future registered text editors. +# +# If you want packages to be able to add functionality to your non-pane text +# editors (such as a search field in a custom user interface element), register +# them for observation via `atom.textEditors.add`. **Important:** When you're +# done using your editor, be sure to call `dispose` on the returned disposable +# to avoid leaking editors. module.exports = class TextEditorRegistry constructor: -> From 7600c4bdf47cd17e0e54e8d620ed33ed89f21bd4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 17:39:11 -0700 Subject: [PATCH 078/198] Avoid wrapper closure by passing callback to forEach directly /cc @wvanlint --- src/text-editor-registry.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-registry.coffee b/src/text-editor-registry.coffee index 943a0d31a..8a17335d4 100644 --- a/src/text-editor-registry.coffee +++ b/src/text-editor-registry.coffee @@ -36,5 +36,5 @@ class TextEditorRegistry # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observe: (callback) -> - @editors.forEach (editor) -> callback(editor) + @editors.forEach(callback) @emitter.on 'did-add-editor', callback From b7f0f794f850aff4e1e32a18ff977002a3b365db Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Mon, 29 Feb 2016 12:57:03 -0800 Subject: [PATCH 079/198] Don't destroy pane if replacing last pending item Signed-off-by: Michelle Tilley --- spec/workspace-spec.coffee | 28 ++++++++++++++++++++++++++++ src/pane.coffee | 8 ++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 2e15431b2..b39a07a6c 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -623,6 +623,34 @@ describe "Workspace", -> expect(pane.getItems().length).toBe 2 expect(pane.getItems()).toEqual [editor1, editor2] + describe "when replacing a pending item which is the last item in a second pane", -> + it "does not destory the pane even if core.destroyEmptyPanes is on", -> + atom.config.set('core.destroyEmptyPanes', true) + editor1 = null + editor2 = null + leftPane = atom.workspace.getActivePane() + rightPane = null + + waitsForPromise -> + atom.workspace.open('sample.js', pending: true, split: 'right').then (o) -> + editor1 = o + rightPane = atom.workspace.getActivePane() + spyOn rightPane, "destroyed" + + runs -> + expect(leftPane).not.toBe rightPane + expect(atom.workspace.getActivePane()).toBe rightPane + expect(atom.workspace.getActivePane().getItems().length).toBe 1 + expect(rightPane.getPendingItem()).toBe editor1 + + waitsForPromise -> + atom.workspace.open('sample.txt', pending: true).then (o) -> + editor2 = o + + runs -> + expect(rightPane.getPendingItem()).toBe editor2 + expect(rightPane.destroyed.callCount).toBe 0 + describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> pane = workspace.getActivePane() diff --git a/src/pane.coffee b/src/pane.coffee index 25f421934..b944f763c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -423,10 +423,6 @@ class Pane extends Model return if item in @items - pendingItem = @getPendingItem() - @destroyItem(pendingItem) if pendingItem? - @setPendingItem(item) if pending - if typeof item.onDidDestroy is 'function' itemSubscriptions = new CompositeDisposable itemSubscriptions.add item.onDidDestroy => @removeItem(item, false) @@ -437,6 +433,10 @@ class Pane extends Model @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) + pendingItem = @getPendingItem() + @destroyItem(pendingItem) if pendingItem? + @setPendingItem(item) if pending + @emitter.emit 'did-add-item', {item, index, moved} @setActiveItem(item) unless @getActiveItem()? item From a55ad00ac1cf3995447cfed0c7ad12269fcfe7ee Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 18:51:12 -0700 Subject: [PATCH 080/198] Fix option pass-through in Selection:: and Cursor::autoscroll --- src/selection.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/selection.coffee b/src/selection.coffee index c4046677b..e208ea55a 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -810,11 +810,11 @@ class Selection extends Model @wordwise = false @linewise = false - autoscroll: -> + autoscroll: (options) -> if @marker.hasTail() - @editor.scrollToScreenRange(@getScreenRange(), reversed: @isReversed()) + @editor.scrollToScreenRange(@getScreenRange(), Object.assign({reversed: @isReversed()}, options)) else - @cursor.autoscroll() + @cursor.autoscroll(options) clearAutoscroll: -> From 7f744681c3c792e0bb01beca71ad5eedc3fddcb7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 18:51:36 -0700 Subject: [PATCH 081/198] Simplify consolidateSelections spec to test autoscroll with events --- spec/text-editor-spec.coffee | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 74875a7f5..1c0d3ad02 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -2141,32 +2141,21 @@ describe "TextEditor", -> expect(editor.getSelections()).toEqual [selection, selection2, selection3, selection4] [selection, selection2, selection3, selection4] - it "destroys all selections but the least recent, returning true if any selections were destroyed", -> + it "destroys all selections but the oldest selection and autoscrolls to it, returning true if any selections were destroyed", -> [selection1] = makeMultipleSelections() + autoscrollEvents = [] + editor.onDidRequestAutoscroll (event) -> autoscrollEvents.push(event) + expect(editor.consolidateSelections()).toBeTruthy() expect(editor.getSelections()).toEqual [selection1] expect(selection1.isEmpty()).toBeFalsy() expect(editor.consolidateSelections()).toBeFalsy() expect(editor.getSelections()).toEqual [selection1] - it "scrolls to the remaining selection", -> - [selection1] = makeMultipleSelections() - - atom.config.set('editor.scrollPastEnd', true) - editor.setHeight(100, true) - editor.setLineHeightInPixels(10) - editor.setFirstVisibleScreenRow(10) - expect(editor.getVisibleRowRange()[0]).toBeGreaterThan(selection1.getBufferRowRange()[1]) - - editor.consolidateSelections() - - waitsForPromise -> - new Promise((resolve) -> window.requestAnimationFrame(resolve)) - - runs -> - expect(editor.getVisibleRowRange()[0]).not.toBeGreaterThan(selection1.getBufferRowRange()[0]) - expect(editor.getVisibleRowRange()[1]).not.toBeLessThan(selection1.getBufferRowRange()[0]) + expect(autoscrollEvents).toEqual([ + {screenRange: selection1.getScreenRange(), options: {center: true, reversed: false}} + ]) describe "when the cursor is moved while there is a selection", -> makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]] From 4e409ef44ae2d406e9e7449014aa3a8d7fc84f9e Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 29 Feb 2016 19:43:59 -0700 Subject: [PATCH 082/198] :arrow_up: image-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9ba72dc83..da7c700e5 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", - "image-view": "0.56.0", + "image-view": "0.57.0", "incompatible-packages": "0.25.1", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", From fdac1e466369c11fb72a331b645da50b9975b52b Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Thu, 8 Jan 2015 02:07:44 +0900 Subject: [PATCH 083/198] :bug: Treat empty comment line as comment and add tests (Fix #4140) --- spec/fixtures/sample-with-comments.js | 13 +++++++- spec/language-mode-spec.coffee | 45 ++++++++++++++++++++++++--- src/language-mode.coffee | 2 -- src/tokenized-line.coffee | 1 - 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/spec/fixtures/sample-with-comments.js b/spec/fixtures/sample-with-comments.js index c10d42232..b40ddc890 100644 --- a/spec/fixtures/sample-with-comments.js +++ b/spec/fixtures/sample-with-comments.js @@ -9,12 +9,23 @@ var quicksort = function () { // Wowza if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = []; + /* + This is a multiline comment block with + an empty line inside of it. + + Awesome. + */ while(items.length > 0) { current = items.shift(); current < pivot ? left.push(current) : right.push(current); } + // This is a collection of + // single line comments + + // ...with an empty line + // among it, geez! return sort(left).concat(pivot).concat(sort(right)); }; // this is a single-line comment return sort(Array.apply(this, arguments)); -}; \ No newline at end of file +}; diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index cd32e29c7..d4d97d2de 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -1,4 +1,4 @@ -describe "LanguageMode", -> +fdescribe "LanguageMode", -> [editor, buffer, languageMode] = [] afterEach -> @@ -430,7 +430,7 @@ describe "LanguageMode", -> languageMode.foldAll() fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] + expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(1).fold @@ -441,6 +441,14 @@ describe "LanguageMode", -> fold4 = editor.tokenizedLineForScreenRow(3).fold expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8] + fold5 = editor.tokenizedLineForScreenRow(6).fold + expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16] + fold5.destroy(); + + fold6 = editor.tokenizedLineForScreenRow(13).fold + expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22] + fold6.destroy(); + describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> languageMode.foldAllAtIndentLevel(2) @@ -450,19 +458,48 @@ describe "LanguageMode", -> fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(11).fold - expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14] + expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 16] fold2.destroy() + fold3 = editor.tokenizedLineForScreenRow(17).fold + expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [17, 20] + fold3.destroy() + + fold4 = editor.tokenizedLineForScreenRow(21).fold + expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [21, 22] + fold4.destroy() + + fold5 = editor.tokenizedLineForScreenRow(24).fold + expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [24, 25] + fold5.destroy() + it "does not fold anything but the indentLevel", -> languageMode.foldAllAtIndentLevel(0) fold1 = editor.tokenizedLineForScreenRow(0).fold - expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] + expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30] fold1.destroy() fold2 = editor.tokenizedLineForScreenRow(5).fold expect(fold2).toBeFalsy() + describe ".isFoldableAtBufferRow(bufferRow)", -> + it "returns true if the line starts a multi-line comment", -> + expect(languageMode.isFoldableAtBufferRow(1)).toBe true + expect(languageMode.isFoldableAtBufferRow(6)).toBe true + expect(languageMode.isFoldableAtBufferRow(8)).toBe false + expect(languageMode.isFoldableAtBufferRow(11)).toBe true + expect(languageMode.isFoldableAtBufferRow(15)).toBe false + expect(languageMode.isFoldableAtBufferRow(17)).toBe true + expect(languageMode.isFoldableAtBufferRow(21)).toBe true + expect(languageMode.isFoldableAtBufferRow(24)).toBe true + expect(languageMode.isFoldableAtBufferRow(28)).toBe false + + it "does not return true for a line in the middle of a comment that's followed by an indented line", -> + expect(languageMode.isFoldableAtBufferRow(7)).toBe false + editor.buffer.insert([8, 0], ' ') + expect(languageMode.isFoldableAtBufferRow(7)).toBe false + describe "css", -> beforeEach -> waitsForPromise -> diff --git a/src/language-mode.coffee b/src/language-mode.coffee index dc5003cac..4824431bf 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -147,13 +147,11 @@ class LanguageMode if bufferRow > 0 for currentRow in [bufferRow-1..0] by -1 - break if @buffer.isRowBlank(currentRow) break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() startRow = currentRow if bufferRow < @buffer.getLastRow() for currentRow in [bufferRow+1..@buffer.getLastRow()] by 1 - break if @buffer.isRowBlank(currentRow) break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() endRow = currentRow diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index bf6a6dd2b..c1ac4caff 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -498,7 +498,6 @@ class TokenizedLine while iterator.next() scopes = iterator.getScopes() continue if scopes.length is 1 - continue unless NonWhitespaceRegex.test(iterator.getText()) for scope in scopes if CommentScopeRegex.test(scope) @isCommentLine = true From 6d00d5e9798e0f1e08cf2af04036f1455e15923c Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Thu, 8 Jan 2015 02:18:23 +0900 Subject: [PATCH 084/198] Revert focus on language-mode-spec.coffee --- spec/language-mode-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index d4d97d2de..6ae21a8e6 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -1,4 +1,4 @@ -fdescribe "LanguageMode", -> +describe "LanguageMode", -> [editor, buffer, languageMode] = [] afterEach -> From a59ee5dec89fadbd469bbdd0595293564fd16751 Mon Sep 17 00:00:00 2001 From: Koki Takahashi Date: Wed, 3 Jun 2015 02:12:40 +0900 Subject: [PATCH 085/198] Remove trailing semicolon --- spec/language-mode-spec.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index 6ae21a8e6..26bb19b0e 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -443,11 +443,11 @@ describe "LanguageMode", -> fold5 = editor.tokenizedLineForScreenRow(6).fold expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16] - fold5.destroy(); + fold5.destroy() fold6 = editor.tokenizedLineForScreenRow(13).fold expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22] - fold6.destroy(); + fold6.destroy() describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> From 3b135c5b7f4456319847d5af89806496bdfe1b38 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 1 Mar 2016 09:43:25 +0100 Subject: [PATCH 086/198] :arrow_up: autocomplete-plus --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da7c700e5..1c55268dd 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.29.0", + "autocomplete-plus": "2.29.1", "autocomplete-snippets": "1.10.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 4cd7cbda021b8673a6a93eb31e9e8c5e1106b74d Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 10:53:31 -0500 Subject: [PATCH 087/198] Test ignoreScrollPastEnd. --- spec/text-editor-presenter-spec.coffee | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 05ac87c0c..62d1e4747 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -635,16 +635,28 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> presenter.setExplicitHeight(500) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe 500 - it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) - expectStateUpdate presenter, -> presenter.setScrollTop(300) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + describe "scrollPastEnd", -> + it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3) - expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) - expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but ignoreScrollPastEnd is true", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, ignoreScrollPastEnd: true) + expectStateUpdate presenter, -> presenter.setScrollTop(300) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + + expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) + expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight describe ".scrollTop", -> it "tracks the value of ::scrollTop", -> From d929543aa2c9d8a0d2e8a771247059ebd90b5326 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 10:56:03 -0500 Subject: [PATCH 088/198] Test for getElement. --- spec/view-registry-spec.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/spec/view-registry-spec.coffee b/spec/view-registry-spec.coffee index 16672b25d..68a482b48 100644 --- a/spec/view-registry-spec.coffee +++ b/spec/view-registry-spec.coffee @@ -23,6 +23,15 @@ describe "ViewRegistry", -> component = new TestComponent expect(registry.getView(component)).toBe component.element + describe "when passed an object with a getElement function", -> + it "returns the return value of getElement if it's an instance of HTMLElement", -> + class TestComponent + getElement: -> + @myElement ?= document.createElement('div') + + component = new TestComponent + expect(registry.getView(component)).toBe component.myElement + describe "when passed a model object", -> describe "when a view provider is registered matching the object's constructor", -> it "constructs a view element and assigns the model on it", -> From e0d44aad5df4abd92082e3f99dc1ae66001a7a00 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 11:07:55 -0500 Subject: [PATCH 089/198] Test getElement. --- spec/text-editor-spec.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 1c0d3ad02..ae8ec54a9 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5828,3 +5828,7 @@ describe "TextEditor", -> screenRange: marker1.getRange(), rangeIsReversed: false } + + describe "::getElement", -> + it "returns an element", -> + expect(editor.getElement() instanceof HTMLElement).toBe(true) From e1c5aad0a6972ce9950363770db6af30d56677c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 11:43:38 -0500 Subject: [PATCH 090/198] Test ignoreInvisibles and grammarName. --- spec/text-editor-spec.coffee | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index ae8ec54a9..c75084fd5 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5829,6 +5829,29 @@ describe "TextEditor", -> rangeIsReversed: false } + describe "when the editor is constructed with the ignoreInvisibles option set to true", -> + beforeEach -> + atom.workspace.destroyActivePane() + waitsForPromise -> + atom.workspace.open('sample.js', ignoreInvisibles: true).then (o) -> editor = o + + it "ignores invisibles even if editor.showInvisibles is true", -> + atom.config.set('editor.showInvisibles', true) + invisibles = editor.tokenizedLineForScreenRow(0).invisibles + expect(invisibles).toBe(null) + + describe "when the editor is constructed with the grammarName option set", -> + beforeEach -> + atom.workspace.destroyActivePane() + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + + waitsForPromise -> + atom.workspace.open('sample.js', grammarName: 'source.coffee').then (o) -> editor = o + + it "sets the grammar", -> + expect(editor.getGrammar().name).toBe 'CoffeeScript' + describe "::getElement", -> it "returns an element", -> expect(editor.getElement() instanceof HTMLElement).toBe(true) From da0ce2a7bde9003b0b432247a13e9d4673729f80 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Tue, 1 Mar 2016 18:54:11 +0100 Subject: [PATCH 091/198] :arrow_up: language-xml --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c55268dd..7ff3df275 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "language-text": "0.7.0", "language-todo": "0.27.0", "language-toml": "0.18.0", - "language-xml": "0.34.3", + "language-xml": "0.34.4", "language-yaml": "0.25.1" }, "private": true, From e1f7dd92238026f43bf7c4c98b29091c9e9b6260 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 1 Mar 2016 15:43:45 -0500 Subject: [PATCH 092/198] :arrow_up: nodegit@0.11.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8830a3f98..5df436fb5 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.5", + "nodegit": "0.11.6", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 3716aaf00bc080ba91a2743c69fc4bbf72e23c83 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 2 Feb 2016 11:56:05 -0800 Subject: [PATCH 093/198] Spike out an update wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can expose just a few event subscription methods on atom.update to take care of what most packages (e.g. About) would be interested in. Of course the updater runs on the main thread so we’re proxying them through IPC. It’s fine. --- src/application-delegate.coffee | 28 +++++++++++++ src/atom-environment.coffee | 3 ++ src/browser/auto-update-manager.coffee | 14 +++++++ src/update.js | 56 ++++++++++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/update.js diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index cc1f4c946..a4262965f 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -182,6 +182,34 @@ class ApplicationDelegate new Disposable -> ipcRenderer.removeListener('message', outerCallback) + onDidBeginCheckingForUpdate: (callback) -> + outerCallback = (message, detail) -> + if message is 'checking-for-update' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onDidCompleteDownloadingUpdate: (callback) -> + outerCallback = (message, detail) -> + if message is 'update-downloaded' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onUpdateNotAvailable: (callback) -> + outerCallback = (message, detail) -> + if message is 'update-not-available' + callback(detail) + + ipc.on('message', outerCallback) + new Disposable -> + ipc.removeListener('message', outerCallback) + + onApplicationMenuCommand: (callback) -> outerCallback = (event, args...) -> callback(args...) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 0ee12fe93..e6830ceaa 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -115,6 +115,9 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null + # Public: An {Update} instance + update: null + saveStateDebounceInterval: 1000 ### diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 2df338761..c58d317cb 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -39,12 +39,19 @@ class AutoUpdateManager autoUpdater.on 'checking-for-update', => @setState(CheckingState) + @emitWindowEvent('checking-for-update') autoUpdater.on 'update-not-available', => @setState(NoUpdateAvailableState) autoUpdater.on 'update-available', => @setState(DownladingState) + # We use sendMessage to send an event called 'update-available' below + # once the update download is complete. This mismatch between the electron + # autoUpdater events is unfortunate but in the interest of not changing the + # one existing event handled by applicationDelegate + @emitWindowEvent('did-begin-downloading-update') + @emit('did-begin-download') autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) => @setState(UpdateAvailableState) @@ -66,10 +73,16 @@ class AutoUpdateManager emitUpdateAvailableEvent: (windows...) -> return unless @releaseVersion? + @emitWindowEvent('update-available', {@releaseVersion}) for atomWindow in windows atomWindow.sendMessage('update-available', {@releaseVersion}) return + emitWindowEvent: (eventName, windows, payload) -> + for atomWindow in windows + atomWindow.sendMessage(eventName, payload) + return + setState: (state) -> return if @state is state @state = state @@ -93,6 +106,7 @@ class AutoUpdateManager @checkForUpdatesIntervalID = null check: ({hidePopups}={}) -> + console.log 'checking for update' unless hidePopups autoUpdater.once 'update-not-available', @onUpdateNotAvailable autoUpdater.once 'error', @onUpdateError diff --git a/src/update.js b/src/update.js new file mode 100644 index 000000000..fcadbc21c --- /dev/null +++ b/src/update.js @@ -0,0 +1,56 @@ +'use babel' + +import {Emitter} from 'event-kit' + +export default class Update { + constructor () { + this.subscriptions = new CompositeDisposable() + this.emitter = new Emitter() + } + + initialize () { + atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }) + atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + this.emitter.emit('did-begin-checking-for-update') + }) + atom.applicationDelegate.onUpdateAvailable(() => { + this.emitter.emit('did-complete-downloading-update') + }) + } + + dispose () { + this.subscriptions.dispose() + } + + onDidBeginCheckingForUpdate (callback) { + this.subscriptions.add( + this.emitter.on('did-begin-checking-for-update', callback) + ) + } + + onDidBeginDownload (callback) { + this.subscriptions.add( + this.emitter.on('did-begin-downloading-update', callback) + ) + } + + onDidCompleteDownload (callback) { + this.subscriptions.add( + this.emitter.on('did-complete-downloading-update', callback) + ) + } + + onUpdateNotAvailable (callback) { + this.subscriptions.add() + } + + check () { + // TODO + } + + getState () { + // TODO + } +} From 1eaf30fae979ecda2b6cd6e5df99d31069e01f22 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 2 Feb 2016 22:34:11 -0800 Subject: [PATCH 094/198] Add a few more things before stepping aside to work on another issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …maybe rebase this away… --- spec/update-spec.js | 37 ++++++++++++++++++++++++++ src/application-delegate.coffee | 4 +-- src/browser/auto-update-manager.coffee | 11 ++++---- src/update.js | 12 +++++++-- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 spec/update-spec.js diff --git a/spec/update-spec.js b/spec/update-spec.js new file mode 100644 index 000000000..9e12bab48 --- /dev/null +++ b/spec/update-spec.js @@ -0,0 +1,37 @@ +'use babel' + +import Update from '../src/update' +import remote from 'remote' + +fdescribe('Update', () => { + describe('::initialize', () => { + it('subscribes to appropriate applicationDelegate events', () => { + const update = new Update() + update.initialize() + + const downloadingSpy = jasmine.createSpy('downloadingSpy') + const checkingSpy = jasmine.createSpy('checkingSpy') + const noUpdateSpy = jasmine.createSpy('noUpdateSpy') + + update.onDidBeginCheckingForUpdate(checkingSpy) + update.onDidBeginDownload(downloadingSpy) + update.onUpdateNotAvailable(noUpdateSpy) + + const webContents = remote.getCurrentWebContents() + // AutoUpdateManager sends these from main process land + webContents.send('update-available', {releaseVersion: '1.2.3'}) + webContents.send('did-begin-downloading-update') + webContents.send('checking-for-update') + webContents.send('update-not-available') + + waitsFor(() => { + noUpdateSpy.callCount > 0 + }) + runs(() => { + expect(downloadingSpy.callCount).toBe(1) + expect(checkingSpy.callCount).toBe(1) + expect(noUpdateSpy.callCount).toBe(1) + }) + }) + }) +}) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index a4262965f..8368e10ec 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -175,7 +175,7 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-available' + if message is 'did-begin-downloading-update' callback(detail) ipcRenderer.on('message', outerCallback) @@ -193,7 +193,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (message, detail) -> - if message is 'update-downloaded' + if message is 'update-available' callback(detail) ipc.on('message', outerCallback) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c58d317cb..064ccf27c 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -43,6 +43,7 @@ class AutoUpdateManager autoUpdater.on 'update-not-available', => @setState(NoUpdateAvailableState) + @emitWindowEvent('update-not-available') autoUpdater.on 'update-available', => @setState(DownladingState) @@ -55,7 +56,7 @@ class AutoUpdateManager autoUpdater.on 'update-downloaded', (event, releaseNotes, @releaseVersion) => @setState(UpdateAvailableState) - @emitUpdateAvailableEvent(@getWindows()...) + @emitUpdateAvailableEvent() @config.onDidChange 'core.automaticallyUpdate', ({newValue}) => if newValue @@ -71,15 +72,13 @@ class AutoUpdateManager when 'linux' @setState(UnsupportedState) - emitUpdateAvailableEvent: (windows...) -> + emitUpdateAvailableEvent: -> return unless @releaseVersion? @emitWindowEvent('update-available', {@releaseVersion}) - for atomWindow in windows - atomWindow.sendMessage('update-available', {@releaseVersion}) return - emitWindowEvent: (eventName, windows, payload) -> - for atomWindow in windows + emitWindowEvent: (eventName, payload) -> + for atomWindow in @getWindows() atomWindow.sendMessage(eventName, payload) return diff --git a/src/update.js b/src/update.js index fcadbc21c..4abffc0b0 100644 --- a/src/update.js +++ b/src/update.js @@ -1,6 +1,6 @@ 'use babel' -import {Emitter} from 'event-kit' +import {Emitter, CompositeDisposable} from 'event-kit' export default class Update { constructor () { @@ -42,8 +42,16 @@ export default class Update { ) } + onUpdateAvailable (callback) { + this.subscriptions.add( + this.emitter.on('update-available', callback) + ) + } + onUpdateNotAvailable (callback) { - this.subscriptions.add() + this.subscriptions.add( + this.emitter.on('update-not-available', callback) + ) } check () { From 6505c650089a9d63b0529000603fcf4e4a4d3473 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:20:24 -0800 Subject: [PATCH 095/198] add out/ to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6eec21c2a..bce6c56d3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ debug.log docs/output docs/includes spec/fixtures/evil-files/ +out/ From 44d78327458bf65b0be90c7ec4c9bccf5ee3a385 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:20:56 -0800 Subject: [PATCH 096/198] add @update to AtomEnvironment --- src/atom-environment.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e6830ceaa..31562a392 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -41,6 +41,7 @@ TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' TextEditorRegistry = require './text-editor-registry' +Update = require './update' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -191,6 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry + @update = new Update() @config.load() From 6fce680a2890b698eea10a056f85dc63ae0e67a3 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:21:46 -0800 Subject: [PATCH 097/198] Send check-for-update message over ipc --- src/browser/atom-application.coffee | 3 +++ src/browser/auto-update-manager.coffee | 3 ++- src/update.js | 7 ++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 40b04c3a1..ebcc9717b 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -304,6 +304,9 @@ class AtomApplication ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) + ipcMain.on 'check-for-update', => + @autoUpdateManager.check() + setupDockMenu: -> if process.platform is 'darwin' dockMenu = Menu.buildFromTemplate [ diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 064ccf27c..c17d9aead 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -3,6 +3,7 @@ _ = require 'underscore-plus' Config = require '../config' {EventEmitter} = require 'events' path = require 'path' +ipc = require 'ipc' IdleState = 'idle' CheckingState = 'checking' @@ -47,7 +48,7 @@ class AutoUpdateManager autoUpdater.on 'update-available', => @setState(DownladingState) - # We use sendMessage to send an event called 'update-available' below + # We use sendMessage to send an event called 'update-available' in 'update-downloaded' # once the update download is complete. This mismatch between the electron # autoUpdater events is unfortunate but in the interest of not changing the # one existing event handled by applicationDelegate diff --git a/src/update.js b/src/update.js index 4abffc0b0..452a3647f 100644 --- a/src/update.js +++ b/src/update.js @@ -1,6 +1,7 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' +import ipc from 'ipc' export default class Update { constructor () { @@ -55,10 +56,6 @@ export default class Update { } check () { - // TODO - } - - getState () { - // TODO + ipc.send('check-for-update') } } From a8a5006950c61863ad784bbf8f0a01a98b9ea1d9 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 14:21:56 -0800 Subject: [PATCH 098/198] Add missing subscription event --- src/application-delegate.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 8368e10ec..f70bd7d0c 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -182,6 +182,9 @@ class ApplicationDelegate new Disposable -> ipcRenderer.removeListener('message', outerCallback) + onDidBeginDownloadingUpdate: (callback) -> + @onUpdateAvailable(callback) + onDidBeginCheckingForUpdate: (callback) -> outerCallback = (message, detail) -> if message is 'checking-for-update' From bdb9866ff1a1b1fa9411e261a43357c15159e351 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 17:15:05 -0800 Subject: [PATCH 099/198] remove errant log statement --- src/browser/auto-update-manager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index c17d9aead..2b6539bd8 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -106,7 +106,6 @@ class AutoUpdateManager @checkForUpdatesIntervalID = null check: ({hidePopups}={}) -> - console.log 'checking for update' unless hidePopups autoUpdater.once 'update-not-available', @onUpdateNotAvailable autoUpdater.once 'error', @onUpdateError From 6d77e97901e7b304a75afacd59fec05e75c0bb14 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Wed, 3 Feb 2016 17:32:54 -0800 Subject: [PATCH 100/198] The app delegate uses the `message` channel here. --- spec/update-spec.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 9e12bab48..8c8247538 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -18,11 +18,10 @@ fdescribe('Update', () => { update.onUpdateNotAvailable(noUpdateSpy) const webContents = remote.getCurrentWebContents() - // AutoUpdateManager sends these from main process land - webContents.send('update-available', {releaseVersion: '1.2.3'}) - webContents.send('did-begin-downloading-update') - webContents.send('checking-for-update') - webContents.send('update-not-available') + webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) + webContents.send('message', 'did-begin-downloading-update') + webContents.send('message', 'checking-for-update') + webContents.send('message', 'update-not-available') waitsFor(() => { noUpdateSpy.callCount > 0 From e6a86d38d1d9a841fb7d7fffedab344fe8c4e5d2 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Fri, 5 Feb 2016 12:11:18 -0800 Subject: [PATCH 101/198] this doesn't work --- spec/update-spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/update-spec.js b/spec/update-spec.js index 8c8247538..4d66da666 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -17,6 +17,7 @@ fdescribe('Update', () => { update.onDidBeginDownload(downloadingSpy) update.onUpdateNotAvailable(noUpdateSpy) + // This doesn't work const webContents = remote.getCurrentWebContents() webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) webContents.send('message', 'did-begin-downloading-update') From 004a0e870d83b3b067964890c280e1118844b5c2 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:40:18 -0800 Subject: [PATCH 102/198] Add ApplicationDelegate listener disposables to subscriptions --- src/update.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/update.js b/src/update.js index 452a3647f..b4699fbec 100644 --- a/src/update.js +++ b/src/update.js @@ -10,15 +10,26 @@ export default class Update { } initialize () { - atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { - this.emitter.emit('did-begin-downloading-update') - }) - atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { - this.emitter.emit('did-begin-checking-for-update') - }) - atom.applicationDelegate.onUpdateAvailable(() => { - this.emitter.emit('did-complete-downloading-update') - }) + this.subscriptions.add( + atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + this.emitter.emit('did-begin-checking-for-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { + this.emitter.emit('did-complete-downloading-update') + }) + ) + this.subscriptions.add( + atom.applicationDelegate.onUpdateNotAvailable(() => { + this.emitter.emit('update-not-available') + }) + ) } dispose () { From adc086d4859ed63ef031bb25caa2ec19bdfe69a8 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:41:23 -0800 Subject: [PATCH 103/198] Add specs for Update methods (::check left TODO) --- spec/update-spec.js | 102 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 4d66da666..0f2f66289 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -2,36 +2,120 @@ import Update from '../src/update' import remote from 'remote' +import ipc from 'ipc' fdescribe('Update', () => { + let update + + afterEach(() => { + update.dispose() + }) + describe('::initialize', () => { it('subscribes to appropriate applicationDelegate events', () => { - const update = new Update() - update.initialize() + update = new Update() const downloadingSpy = jasmine.createSpy('downloadingSpy') const checkingSpy = jasmine.createSpy('checkingSpy') const noUpdateSpy = jasmine.createSpy('noUpdateSpy') + const completedDownloadSpy = jasmine.createSpy('completedDownloadSpy') - update.onDidBeginCheckingForUpdate(checkingSpy) - update.onDidBeginDownload(downloadingSpy) - update.onUpdateNotAvailable(noUpdateSpy) + update.emitter.on('did-begin-checking-for-update', checkingSpy) + update.emitter.on('did-begin-downloading-update', downloadingSpy) + update.emitter.on('did-complete-downloading-update', completedDownloadSpy) + update.emitter.on('update-not-available', noUpdateSpy) + + update.initialize() - // This doesn't work const webContents = remote.getCurrentWebContents() - webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) - webContents.send('message', 'did-begin-downloading-update') webContents.send('message', 'checking-for-update') + webContents.send('message', 'did-begin-downloading-update') + webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) webContents.send('message', 'update-not-available') waitsFor(() => { - noUpdateSpy.callCount > 0 + return noUpdateSpy.callCount > 0 }) + runs(() => { expect(downloadingSpy.callCount).toBe(1) expect(checkingSpy.callCount).toBe(1) expect(noUpdateSpy.callCount).toBe(1) + expect(completedDownloadSpy.callCount).toBe(1) }) }) }) + + beforeEach(() => { + update = new Update() + update.initialize() + }) + + describe('::onDidBeginCheckingForUpdate', () => { + it('subscribes to "did-begin-checking-for-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidBeginCheckingForUpdate(spy) + update.emitter.emit('did-begin-checking-for-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onDidBeginDownload', () => { + it('subscribes to "did-begin-downloading-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidBeginDownload(spy) + update.emitter.emit('did-begin-downloading-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onDidCompleteDownload', () => { + it('subscribes to "did-complete-downloading-update" event', () => { + const spy = jasmine.createSpy('spy') + update.onDidCompleteDownload(spy) + update.emitter.emit('did-complete-downloading-update') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onUpdateNotAvailable', () => { + it('subscribes to "update-not-available" event', () => { + const spy = jasmine.createSpy('spy') + update.onUpdateNotAvailable(spy) + update.emitter.emit('update-not-available') + expect(spy.callCount).toBe(1) + }) + }) + + describe('::onUpdateAvailable', () => { + it('subscribes to "update-available" event', () => { + const spy = jasmine.createSpy('spy') + update.onUpdateAvailable(spy) + update.emitter.emit('update-available') + expect(spy.callCount).toBe(1) + }) + }) + + // TODO: spec is timing out. spy is not called + // describe('::check', () => { + // it('sends "check-for-update" event', () => { + // const spy = jasmine.createSpy('spy') + // ipc.on('check-for-update', () => { + // spy() + // }) + // update.check() + // waitsFor(() => { + // return spy.callCount > 0 + // }) + // }) + // }) + + describe('::dispose', () => { + it('disposes of subscriptions', () => { + expect(update.subscriptions.disposables).not.toBeNull() + update.dispose() + expect(update.subscriptions.disposables).toBeNull() + }) + }) + }) From 3e29cd57626945827d6930bfea5d31760184fa8a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 18 Feb 2016 23:50:27 -0800 Subject: [PATCH 104/198] Merge branch 'master' into dh-expose-updates --- src/application-delegate.coffee | 18 +++++++++--------- src/browser/atom-application.coffee | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index f70bd7d0c..2aaeb35ff 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -186,31 +186,31 @@ class ApplicationDelegate @onUpdateAvailable(callback) onDidBeginCheckingForUpdate: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'checking-for-update' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onDidCompleteDownloadingUpdate: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'update-available' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onUpdateNotAvailable: (callback) -> - outerCallback = (message, detail) -> + outerCallback = (event, message, detail) -> if message is 'update-not-available' callback(detail) - ipc.on('message', outerCallback) + ipcRenderer.on('message', outerCallback) new Disposable -> - ipc.removeListener('message', outerCallback) + ipcRenderer.removeListener('message', outerCallback) onApplicationMenuCommand: (callback) -> diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index ebcc9717b..fad69b967 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -307,6 +307,9 @@ class AtomApplication ipcMain.on 'check-for-update', => @autoUpdateManager.check() + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> + event.sender.devToolsWebContents?.executeJavaScript(code) + setupDockMenu: -> if process.platform is 'darwin' dockMenu = Menu.buildFromTemplate [ From 5cda45b5c73b893e1352d099aa7e3f9e3ecb611d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:36:09 -0800 Subject: [PATCH 105/198] =?UTF-8?q?Use=20ipcRenderer=20from=20=E2=80=98ele?= =?UTF-8?q?ctron=E2=80=99=20rather=20than=20=E2=80=98pic=E2=80=99=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/update.js b/src/update.js index b4699fbec..5389c35e8 100644 --- a/src/update.js +++ b/src/update.js @@ -1,7 +1,7 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' -import ipc from 'ipc' +import {ipcRenderer} from 'electron' export default class Update { constructor () { @@ -67,6 +67,6 @@ export default class Update { } check () { - ipc.send('check-for-update') + ipcRenderer.send('check-for-update') } } From 16ebefca3f2a7c4fa23ac0106009ca413c7541b3 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:36:38 -0800 Subject: [PATCH 106/198] =?UTF-8?q?Remove=20unnecessary=20=E2=80=98ipc'=20?= =?UTF-8?q?module=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/auto-update-manager.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 2b6539bd8..c8c57cb01 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -3,7 +3,6 @@ _ = require 'underscore-plus' Config = require '../config' {EventEmitter} = require 'events' path = require 'path' -ipc = require 'ipc' IdleState = 'idle' CheckingState = 'checking' From 19d30c7dc955b557927571fbf60038fc446e4a6d Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 12:40:51 -0800 Subject: [PATCH 107/198] Improve specs for `Update` class --- spec/update-spec.js | 104 ++++++++++---------------------------------- 1 file changed, 23 insertions(+), 81 deletions(-) diff --git a/spec/update-spec.js b/spec/update-spec.js index 0f2f66289..bd50113c2 100644 --- a/spec/update-spec.js +++ b/spec/update-spec.js @@ -1,62 +1,29 @@ 'use babel' import Update from '../src/update' -import remote from 'remote' -import ipc from 'ipc' +import {remote} from 'electron' +const electronAutoUpdater = remote.require('electron').autoUpdater -fdescribe('Update', () => { +describe('Update', () => { let update - afterEach(() => { - update.dispose() - }) - - describe('::initialize', () => { - it('subscribes to appropriate applicationDelegate events', () => { - update = new Update() - - const downloadingSpy = jasmine.createSpy('downloadingSpy') - const checkingSpy = jasmine.createSpy('checkingSpy') - const noUpdateSpy = jasmine.createSpy('noUpdateSpy') - const completedDownloadSpy = jasmine.createSpy('completedDownloadSpy') - - update.emitter.on('did-begin-checking-for-update', checkingSpy) - update.emitter.on('did-begin-downloading-update', downloadingSpy) - update.emitter.on('did-complete-downloading-update', completedDownloadSpy) - update.emitter.on('update-not-available', noUpdateSpy) - - update.initialize() - - const webContents = remote.getCurrentWebContents() - webContents.send('message', 'checking-for-update') - webContents.send('message', 'did-begin-downloading-update') - webContents.send('message', 'update-available', {releaseVersion: '1.2.3'}) - webContents.send('message', 'update-not-available') - - waitsFor(() => { - return noUpdateSpy.callCount > 0 - }) - - runs(() => { - expect(downloadingSpy.callCount).toBe(1) - expect(checkingSpy.callCount).toBe(1) - expect(noUpdateSpy.callCount).toBe(1) - expect(completedDownloadSpy.callCount).toBe(1) - }) - }) - }) - beforeEach(() => { update = new Update() update.initialize() }) + afterEach(() => { + update.dispose() + }) + describe('::onDidBeginCheckingForUpdate', () => { it('subscribes to "did-begin-checking-for-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidBeginCheckingForUpdate(spy) - update.emitter.emit('did-begin-checking-for-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('checking-for-update') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -64,8 +31,10 @@ fdescribe('Update', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidBeginDownload(spy) - update.emitter.emit('did-begin-downloading-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('update-available') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -73,8 +42,10 @@ fdescribe('Update', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') update.onDidCompleteDownload(spy) - update.emitter.emit('did-complete-downloading-update') - expect(spy.callCount).toBe(1) + electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + waitsFor(() => { + return spy.callCount === 1 + }) }) }) @@ -82,39 +53,10 @@ fdescribe('Update', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') update.onUpdateNotAvailable(spy) - update.emitter.emit('update-not-available') - expect(spy.callCount).toBe(1) - }) - }) - - describe('::onUpdateAvailable', () => { - it('subscribes to "update-available" event', () => { - const spy = jasmine.createSpy('spy') - update.onUpdateAvailable(spy) - update.emitter.emit('update-available') - expect(spy.callCount).toBe(1) - }) - }) - - // TODO: spec is timing out. spy is not called - // describe('::check', () => { - // it('sends "check-for-update" event', () => { - // const spy = jasmine.createSpy('spy') - // ipc.on('check-for-update', () => { - // spy() - // }) - // update.check() - // waitsFor(() => { - // return spy.callCount > 0 - // }) - // }) - // }) - - describe('::dispose', () => { - it('disposes of subscriptions', () => { - expect(update.subscriptions.disposables).not.toBeNull() - update.dispose() - expect(update.subscriptions.disposables).toBeNull() + electronAutoUpdater.emit('update-not-available') + waitsFor(() => { + return spy.callCount === 1 + }) }) }) From dd53c6f85644701cd0c6b61e7cfca5c69d098df0 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Fri, 19 Feb 2016 15:35:42 -0800 Subject: [PATCH 108/198] Use `onDidCompleteDownloadingUpdate` in `listenForUpdates` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gets a bit confusing… It was formerly `@applicationDelegate.onUpdateAvailable`, but `::onUpdateAvailable` listens for the `did-begin-downloading-update` event and `::onDidCompleteDownloadingUpdate` listens for the `update-available` event. Note that ‘available’ here means successfully downloaded and ready to be used and NOT available to be downloaded. --- src/atom-environment.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 31562a392..b9527b947 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -891,7 +891,8 @@ class AtomEnvironment extends Model @emitter.emit 'update-available', details listenForUpdates: -> - @disposables.add(@applicationDelegate.onUpdateAvailable(@updateAvailable.bind(this))) + # listen for updates available locally (that have been successfully downloaded) + @disposables.add(@applicationDelegate.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 342f72b6a14a8bd7280133e034ab8fc07e1b3257 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:07:11 -0800 Subject: [PATCH 109/198] Rename `Update` `AutoUpdateManager` --- ...te-spec.js => auto-update-manager-spec.js} | 21 +++++++++---------- src/atom-environment.coffee | 8 +++---- src/{update.js => auto-update-manager.js} | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) rename spec/{update-spec.js => auto-update-manager-spec.js} (75%) rename src/{update.js => auto-update-manager.js} (97%) diff --git a/spec/update-spec.js b/spec/auto-update-manager-spec.js similarity index 75% rename from spec/update-spec.js rename to spec/auto-update-manager-spec.js index bd50113c2..b78830c33 100644 --- a/spec/update-spec.js +++ b/spec/auto-update-manager-spec.js @@ -1,25 +1,25 @@ 'use babel' -import Update from '../src/update' +import AutoUpdateManager from '../src/auto-update-manager' import {remote} from 'electron' const electronAutoUpdater = remote.require('electron').autoUpdater -describe('Update', () => { - let update +fdescribe('AutoUpdateManager (renderer)', () => { + let autoUpdateManager beforeEach(() => { - update = new Update() - update.initialize() + autoUpdateManager = new AutoUpdateManager() + autoUpdateManager.initialize() }) afterEach(() => { - update.dispose() + autoUpdateManager.dispose() }) describe('::onDidBeginCheckingForUpdate', () => { it('subscribes to "did-begin-checking-for-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidBeginCheckingForUpdate(spy) + autoUpdateManager.onDidBeginCheckingForUpdate(spy) electronAutoUpdater.emit('checking-for-update') waitsFor(() => { return spy.callCount === 1 @@ -30,7 +30,7 @@ describe('Update', () => { describe('::onDidBeginDownload', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidBeginDownload(spy) + autoUpdateManager.onDidBeginDownload(spy) electronAutoUpdater.emit('update-available') waitsFor(() => { return spy.callCount === 1 @@ -41,7 +41,7 @@ describe('Update', () => { describe('::onDidCompleteDownload', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - update.onDidCompleteDownload(spy) + autoUpdateManager.onDidCompleteDownload(spy) electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) waitsFor(() => { return spy.callCount === 1 @@ -52,12 +52,11 @@ describe('Update', () => { describe('::onUpdateNotAvailable', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') - update.onUpdateNotAvailable(spy) + autoUpdateManager.onUpdateNotAvailable(spy) electronAutoUpdater.emit('update-not-available') waitsFor(() => { return spy.callCount === 1 }) }) }) - }) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index b9527b947..4fead0da9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -41,7 +41,7 @@ TextEditor = require './text-editor' TextBuffer = require 'text-buffer' Gutter = require './gutter' TextEditorRegistry = require './text-editor-registry' -Update = require './update' +AutoUpdateManager = require './auto-update-manager' WorkspaceElement = require './workspace-element' PanelContainerElement = require './panel-container-element' @@ -116,8 +116,8 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null - # Public: An {Update} instance - update: null + # Public: An {AutoUpdateManager} instance + autoUpdater: null saveStateDebounceInterval: 1000 @@ -192,7 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry - @update = new Update() + @autoUpdater = new AutoUpdateManager @config.load() diff --git a/src/update.js b/src/auto-update-manager.js similarity index 97% rename from src/update.js rename to src/auto-update-manager.js index 5389c35e8..1f0f39f29 100644 --- a/src/update.js +++ b/src/auto-update-manager.js @@ -3,7 +3,7 @@ import {Emitter, CompositeDisposable} from 'event-kit' import {ipcRenderer} from 'electron' -export default class Update { +export default class AutoUpdateManager { constructor () { this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() From e40c91d3538e512004e058b8c21bd53994a36a69 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:08:59 -0800 Subject: [PATCH 110/198] All subscriptions can be in one call --- src/auto-update-manager.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 1f0f39f29..006eadf9e 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -13,19 +13,13 @@ export default class AutoUpdateManager { this.subscriptions.add( atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { this.emitter.emit('did-complete-downloading-update') - }) - ) - this.subscriptions.add( + }), atom.applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) From ddff53bfc56b373c479c4c50c1f4dde6a04c80d7 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:10:56 -0800 Subject: [PATCH 111/198] Postfix ifs --- src/application-delegate.coffee | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index 2aaeb35ff..a853cd8ea 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -166,8 +166,7 @@ class ApplicationDelegate onDidOpenLocations: (callback) -> outerCallback = (event, message, detail) -> - if message is 'open-locations' - callback(detail) + callback(detail) if message is 'open-locations' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -175,8 +174,7 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'did-begin-downloading-update' - callback(detail) + callback(detail) if message is 'did-begin-downloading-update' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -187,8 +185,7 @@ class ApplicationDelegate onDidBeginCheckingForUpdate: (callback) -> outerCallback = (event, message, detail) -> - if message is 'checking-for-update' - callback(detail) + callback(detail) if message is 'checking-for-update' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -196,8 +193,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-available' - callback(detail) + callback(detail) if message is 'update-available' ipcRenderer.on('message', outerCallback) new Disposable -> @@ -205,14 +201,12 @@ class ApplicationDelegate onUpdateNotAvailable: (callback) -> outerCallback = (event, message, detail) -> - if message is 'update-not-available' - callback(detail) + callback(detail) if message is 'update-not-available' ipcRenderer.on('message', outerCallback) new Disposable -> ipcRenderer.removeListener('message', outerCallback) - onApplicationMenuCommand: (callback) -> outerCallback = (event, args...) -> callback(args...) From b481a06cbfb7628d31c3690e9429961da2867e9a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:05 -0800 Subject: [PATCH 112/198] Pass the app delegate into AutoUpdateManager --- spec/auto-update-manager-spec.js | 2 +- src/atom-environment.coffee | 1 + src/auto-update-manager.js | 10 +++++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index b78830c33..93df903de 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -9,7 +9,7 @@ fdescribe('AutoUpdateManager (renderer)', () => { beforeEach(() => { autoUpdateManager = new AutoUpdateManager() - autoUpdateManager.initialize() + autoUpdateManager.initialize(atom.applicationDelegate) }) afterEach(() => { diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4fead0da9..e492c7067 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -193,6 +193,7 @@ class AtomEnvironment extends Model @textEditors = new TextEditorRegistry @autoUpdater = new AutoUpdateManager + @autoUpdater.initialize(@applicationDelegate) @config.load() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 006eadf9e..c0f0b39bb 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -9,18 +9,18 @@ export default class AutoUpdateManager { this.emitter = new Emitter() } - initialize () { + initialize (updateEventEmitter) { this.subscriptions.add( - atom.applicationDelegate.onDidBeginDownloadingUpdate(() => { + updateEventEmitter.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') }), - atom.applicationDelegate.onDidBeginCheckingForUpdate(() => { + updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - atom.applicationDelegate.onDidCompleteDownloadingUpdate(() => { + updateEventEmitter.onDidCompleteDownloadingUpdate(() => { this.emitter.emit('did-complete-downloading-update') }), - atom.applicationDelegate.onUpdateNotAvailable(() => { + updateEventEmitter.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) ) From a876e85899cd2ebb4056d356ae5dc22866f561b8 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:52 -0800 Subject: [PATCH 113/198] check -> checkForUpdate --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index c0f0b39bb..05de08a3a 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -60,7 +60,7 @@ export default class AutoUpdateManager { ) } - check () { + checkForUpdate () { ipcRenderer.send('check-for-update') } } From eaba6943194153c55ad753e5e6137f8f64a254bd Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:14:59 -0800 Subject: [PATCH 114/198] =?UTF-8?q?Let=E2=80=99s=20make=20this=20private?= =?UTF-8?q?=20for=20now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index e492c7067..481e762ca 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -116,7 +116,7 @@ class AtomEnvironment extends Model # Public: A {TextEditorRegistry} instance textEditors: null - # Public: An {AutoUpdateManager} instance + # Private: An {AutoUpdateManager} instance autoUpdater: null saveStateDebounceInterval: 1000 From 3a5c8271629f6ebd9d4b03779f3b019d1bad5bc9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:23:11 -0800 Subject: [PATCH 115/198] Add dispose spec --- spec/auto-update-manager-spec.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 93df903de..6aa791373 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -59,4 +59,29 @@ fdescribe('AutoUpdateManager (renderer)', () => { }) }) }) + + describe('::dispose', () => { + it('subscribes to "update-not-available" event', () => { + const spy = jasmine.createSpy('spy') + const doneIndicator = jasmine.createSpy('spy') + atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) + autoUpdateManager.dispose() + autoUpdateManager.onDidBeginCheckingForUpdate(spy) + autoUpdateManager.onDidBeginDownload(spy) + autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onUpdateNotAvailable(spy) + electronAutoUpdater.emit('checking-for-update') + electronAutoUpdater.emit('update-available') + electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-not-available') + + waitsFor(() => { + return doneIndicator.callCount === 1 + }) + + runs(() => { + expect(spy.callCount).toBe(0) + }) + }) + }) }) From c9293e7733502c0551438a41633031c902ab43c6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:34:31 -0800 Subject: [PATCH 116/198] Pass the version info through the event --- spec/auto-update-manager-spec.js | 7 +++++-- src/auto-update-manager.js | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 6aa791373..e23c1911d 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -42,10 +42,13 @@ fdescribe('AutoUpdateManager (renderer)', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') autoUpdateManager.onDidCompleteDownload(spy) - electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') waitsFor(() => { return spy.callCount === 1 }) + runs(() => { + expect(spy.mostRecentCall.args[0].releaseVersion).toBe('1.2.3') + }) }) }) @@ -72,7 +75,7 @@ fdescribe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onUpdateNotAvailable(spy) electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') - electronAutoUpdater.emit('update-downloaded', null, null, {releaseVersion: '1.2.3'}) + electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') electronAutoUpdater.emit('update-not-available') waitsFor(() => { diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 05de08a3a..f46197f8d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -17,8 +17,8 @@ export default class AutoUpdateManager { updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - updateEventEmitter.onDidCompleteDownloadingUpdate(() => { - this.emitter.emit('did-complete-downloading-update') + updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { + this.emitter.emit('did-complete-downloading-update', details) }), updateEventEmitter.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') From fa0f6f35258b5cbb9fb347c2858ee0ab258d1df5 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:35:35 -0800 Subject: [PATCH 117/198] =?UTF-8?q?Use=20the=20renderer=20AutoUpdateManage?= =?UTF-8?q?r=20in=20atom-environment=E2=80=99s=20update=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 481e762ca..f1fe1b459 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -893,7 +893,7 @@ class AtomEnvironment extends Model listenForUpdates: -> # listen for updates available locally (that have been successfully downloaded) - @disposables.add(@applicationDelegate.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) + @disposables.add(@autoUpdater.onDidCompleteDownload(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 341abbfc261d2b9206cf2d85cdd7bf147d4760cd Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:35:50 -0800 Subject: [PATCH 118/198] Nof. Nope. --- spec/auto-update-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index e23c1911d..ff2fc0682 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -4,7 +4,7 @@ import AutoUpdateManager from '../src/auto-update-manager' import {remote} from 'electron' const electronAutoUpdater = remote.require('electron').autoUpdater -fdescribe('AutoUpdateManager (renderer)', () => { +describe('AutoUpdateManager (renderer)', () => { let autoUpdateManager beforeEach(() => { From e9e372b69e42d0ab8659bc3e446e6df57ce987a0 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:15 -0800 Subject: [PATCH 119/198] Dispose emitter --- spec/auto-update-manager-spec.js | 2 +- src/auto-update-manager.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index ff2fc0682..f4e7622b2 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -68,11 +68,11 @@ describe('AutoUpdateManager (renderer)', () => { const spy = jasmine.createSpy('spy') const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) - autoUpdateManager.dispose() autoUpdateManager.onDidBeginCheckingForUpdate(spy) autoUpdateManager.onDidBeginDownload(spy) autoUpdateManager.onDidCompleteDownload(spy) autoUpdateManager.onUpdateNotAvailable(spy) + autoUpdateManager.dispose() electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index f46197f8d..681978cb6 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -28,6 +28,7 @@ export default class AutoUpdateManager { dispose () { this.subscriptions.dispose() + this.emitter.dispose() } onDidBeginCheckingForUpdate (callback) { From 4afe6df2c4edc8a4bfc8b5db07ee27a88f7df908 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:26 -0800 Subject: [PATCH 120/198] Reorder for consistency --- src/auto-update-manager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 681978cb6..5f37196ed 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -11,12 +11,12 @@ export default class AutoUpdateManager { initialize (updateEventEmitter) { this.subscriptions.add( - updateEventEmitter.onDidBeginDownloadingUpdate(() => { - this.emitter.emit('did-begin-downloading-update') - }), updateEventEmitter.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), + updateEventEmitter.onDidBeginDownloadingUpdate(() => { + this.emitter.emit('did-begin-downloading-update') + }), updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { this.emitter.emit('did-complete-downloading-update', details) }), From b60e1957a88c0a9c2585434f94cbeadc9a51f3b6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 17:48:43 -0800 Subject: [PATCH 121/198] Return the disposables! --- src/auto-update-manager.js | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 5f37196ed..0bb793b44 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -32,33 +32,23 @@ export default class AutoUpdateManager { } onDidBeginCheckingForUpdate (callback) { - this.subscriptions.add( - this.emitter.on('did-begin-checking-for-update', callback) - ) + return this.emitter.on('did-begin-checking-for-update', callback) } onDidBeginDownload (callback) { - this.subscriptions.add( - this.emitter.on('did-begin-downloading-update', callback) - ) + return this.emitter.on('did-begin-downloading-update', callback) } onDidCompleteDownload (callback) { - this.subscriptions.add( - this.emitter.on('did-complete-downloading-update', callback) - ) + return this.emitter.on('did-complete-downloading-update', callback) } onUpdateAvailable (callback) { - this.subscriptions.add( - this.emitter.on('update-available', callback) - ) + return this.emitter.on('update-available', callback) } onUpdateNotAvailable (callback) { - this.subscriptions.add( - this.emitter.on('update-not-available', callback) - ) + return this.emitter.on('update-not-available', callback) } checkForUpdate () { From d3340d070f456c6e181da7e38624cc5af9d3f062 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 23 Feb 2016 18:12:04 -0800 Subject: [PATCH 122/198] =?UTF-8?q?UpdateAvailable=20isn=E2=80=99t=20reall?= =?UTF-8?q?y=20a=20thing=20in=20this=20world?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auto-update-manager.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 0bb793b44..cc3035fdf 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -43,10 +43,6 @@ export default class AutoUpdateManager { return this.emitter.on('did-complete-downloading-update', callback) } - onUpdateAvailable (callback) { - return this.emitter.on('update-available', callback) - } - onUpdateNotAvailable (callback) { return this.emitter.on('update-not-available', callback) } From e477751c1986743882d551d54fa3d2b99bd189a0 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:00:48 -0800 Subject: [PATCH 123/198] Make the autoupdater functions consistently names --- spec/auto-update-manager-spec.js | 12 ++++++------ src/auto-update-manager.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index f4e7622b2..499e2cb3b 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -27,10 +27,10 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::onDidBeginDownload', () => { + describe('::onDidBeginDownloadingUpdate', () => { it('subscribes to "did-begin-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - autoUpdateManager.onDidBeginDownload(spy) + autoUpdateManager.onDidBeginDownloadingUpdate(spy) electronAutoUpdater.emit('update-available') waitsFor(() => { return spy.callCount === 1 @@ -38,10 +38,10 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::onDidCompleteDownload', () => { + describe('::onDidCompleteDownloadingUpdate', () => { it('subscribes to "did-complete-downloading-update" event', () => { const spy = jasmine.createSpy('spy') - autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onDidCompleteDownloadingUpdate(spy) electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') waitsFor(() => { return spy.callCount === 1 @@ -69,8 +69,8 @@ describe('AutoUpdateManager (renderer)', () => { const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) autoUpdateManager.onDidBeginCheckingForUpdate(spy) - autoUpdateManager.onDidBeginDownload(spy) - autoUpdateManager.onDidCompleteDownload(spy) + autoUpdateManager.onDidBeginDownloadingUpdate(spy) + autoUpdateManager.onDidCompleteDownloadingUpdate(spy) autoUpdateManager.onUpdateNotAvailable(spy) autoUpdateManager.dispose() electronAutoUpdater.emit('checking-for-update') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index cc3035fdf..e7852f2d9 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -35,11 +35,11 @@ export default class AutoUpdateManager { return this.emitter.on('did-begin-checking-for-update', callback) } - onDidBeginDownload (callback) { + onDidBeginDownloadingUpdate (callback) { return this.emitter.on('did-begin-downloading-update', callback) } - onDidCompleteDownload (callback) { + onDidCompleteDownloadingUpdate (callback) { return this.emitter.on('did-complete-downloading-update', callback) } From c19296efb753167619a66532ecb6481339415b9d Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:01:23 -0800 Subject: [PATCH 124/198] Add isEnabled function to AutoUpdateManager --- spec/auto-update-manager-spec.js | 32 ++++++++++++++++++++++++++++++++ src/auto-update-manager.js | 27 +++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 499e2cb3b..6e3c037ef 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -63,6 +63,38 @@ describe('AutoUpdateManager (renderer)', () => { }) }) + describe('::isEnabled', () => { + let platform, releaseChannel + it('returns true on OS X and Windows, when in stable', () => { + spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) + spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) + + platform = 'win32' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(true) + + platform = 'win32' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'darwin' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(true) + + platform = 'darwin' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'linux' + releaseChannel = 'stable' + expect(autoUpdateManager.isEnabled()).toBe(false) + + platform = 'linux' + releaseChannel = 'dev' + expect(autoUpdateManager.isEnabled()).toBe(false) + }) + }) + describe('::dispose', () => { it('subscribes to "update-not-available" event', () => { const spy = jasmine.createSpy('spy') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index e7852f2d9..e99ce83d9 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -31,6 +31,18 @@ export default class AutoUpdateManager { this.emitter.dispose() } + checkForUpdate () { + ipcRenderer.send('check-for-update') + } + + quitAndInstallUpdate () { + ipcRenderer.send('install-update') + } + + isEnabled () { + return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') + } + onDidBeginCheckingForUpdate (callback) { return this.emitter.on('did-begin-checking-for-update', callback) } @@ -47,7 +59,18 @@ export default class AutoUpdateManager { return this.emitter.on('update-not-available', callback) } - checkForUpdate () { - ipcRenderer.send('check-for-update') + getPlatform () { + return process.platform + } + + // TODO: We should move this into atom env or something. + getReleaseChannel () { + let version = atom.getVersion() + if (version.indexOf('beta') > -1) { + return 'beta' + } else if (version.indexOf('dev') > -1) { + return 'dev' + } + return 'stable' } } From 1cd530cdf06e4865a91f159fffc0e687ff78610c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:01:44 -0800 Subject: [PATCH 125/198] Add some todo code --- src/auto-update-manager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index e99ce83d9..06b437c3b 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -55,6 +55,12 @@ export default class AutoUpdateManager { return this.emitter.on('did-complete-downloading-update', callback) } + // TODO: When https://github.com/atom/electron/issues/4587 is closed, we can + // add an update-available event. + // onUpdateAvailable (callback) { + // return this.emitter.on('update-available', callback) + // } + onUpdateNotAvailable (callback) { return this.emitter.on('update-not-available', callback) } From 418b1bd8f1bb95c79620d9847518aa33abdb4142 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:33:39 -0800 Subject: [PATCH 126/198] Get rid of the initialize method --- spec/auto-update-manager-spec.js | 5 +++-- src/atom-environment.coffee | 3 +-- src/auto-update-manager.js | 12 +++++------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 6e3c037ef..2b51ed658 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -8,8 +8,9 @@ describe('AutoUpdateManager (renderer)', () => { let autoUpdateManager beforeEach(() => { - autoUpdateManager = new AutoUpdateManager() - autoUpdateManager.initialize(atom.applicationDelegate) + autoUpdateManager = new AutoUpdateManager({ + applicationDelegate: atom.applicationDelegate + }) }) afterEach(() => { diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f1fe1b459..04074cfcb 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -192,8 +192,7 @@ class AtomEnvironment extends Model @themes.workspace = @workspace @textEditors = new TextEditorRegistry - @autoUpdater = new AutoUpdateManager - @autoUpdater.initialize(@applicationDelegate) + @autoUpdater = new AutoUpdateManager({@applicationDelegate}) @config.load() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 06b437c3b..d811996c1 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -4,23 +4,21 @@ import {Emitter, CompositeDisposable} from 'event-kit' import {ipcRenderer} from 'electron' export default class AutoUpdateManager { - constructor () { + constructor ({applicationDelegate}) { this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() - } - initialize (updateEventEmitter) { this.subscriptions.add( - updateEventEmitter.onDidBeginCheckingForUpdate(() => { + applicationDelegate.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - updateEventEmitter.onDidBeginDownloadingUpdate(() => { + applicationDelegate.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') }), - updateEventEmitter.onDidCompleteDownloadingUpdate((details) => { + applicationDelegate.onDidCompleteDownloadingUpdate((details) => { this.emitter.emit('did-complete-downloading-update', details) }), - updateEventEmitter.onUpdateNotAvailable(() => { + applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }) ) From bc138f727e1af484f0110661eafafae6f4861e95 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 16:33:51 -0800 Subject: [PATCH 127/198] Fix usage of the onDidCompleteDownload() method --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 04074cfcb..2122abbbe 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -892,7 +892,7 @@ class AtomEnvironment extends Model listenForUpdates: -> # listen for updates available locally (that have been successfully downloaded) - @disposables.add(@autoUpdater.onDidCompleteDownload(@updateAvailable.bind(this))) + @disposables.add(@autoUpdater.onDidCompleteDownloadingUpdate(@updateAvailable.bind(this))) setBodyPlatformClass: -> @document.body.classList.add("platform-#{process.platform}") From 68951494d2e6c4becfa44be38d90a49f79f7c3b9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Thu, 25 Feb 2016 17:32:02 -0800 Subject: [PATCH 128/198] isEnabled -> platformSupportsUpdates --- spec/auto-update-manager-spec.js | 14 +++++++------- src/auto-update-manager.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 2b51ed658..e12f85c5b 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -64,7 +64,7 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::isEnabled', () => { + describe('::platformSupportsUpdates', () => { let platform, releaseChannel it('returns true on OS X and Windows, when in stable', () => { spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) @@ -72,27 +72,27 @@ describe('AutoUpdateManager (renderer)', () => { platform = 'win32' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(true) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) platform = 'win32' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'darwin' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(true) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) platform = 'darwin' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'linux' releaseChannel = 'stable' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) platform = 'linux' releaseChannel = 'dev' - expect(autoUpdateManager.isEnabled()).toBe(false) + expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) }) }) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index d811996c1..497097864 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -37,7 +37,7 @@ export default class AutoUpdateManager { ipcRenderer.send('install-update') } - isEnabled () { + platformSupportsUpdates () { return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') } From 7a1ad8263aa63438df16247663cf99e5073d4d72 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:37:59 -0800 Subject: [PATCH 129/198] Rename `quitAndInstallUpdate` to `restartAndInstallUpdate` --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 497097864..eae979d9d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -33,7 +33,7 @@ export default class AutoUpdateManager { ipcRenderer.send('check-for-update') } - quitAndInstallUpdate () { + restartAndInstallUpdate () { ipcRenderer.send('install-update') } From 3a32b30d5a98f704a310c96fa1d7bc8309d7d412 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:38:56 -0800 Subject: [PATCH 130/198] Add mechanism to get the AutoUpdateManager's state --- src/auto-update-manager.js | 4 ++++ src/browser/atom-application.coffee | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index eae979d9d..14ec44c29 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -37,6 +37,10 @@ export default class AutoUpdateManager { ipcRenderer.send('install-update') } + getState () { + return ipcRenderer.sendSync('get-auto-update-manager-state') + } + platformSupportsUpdates () { return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') } diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index fad69b967..fdfea5474 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -307,6 +307,9 @@ class AtomApplication ipcMain.on 'check-for-update', => @autoUpdateManager.check() + ipcMain.on 'get-auto-update-manager-state', (event) => + event.returnValue = @autoUpdateManager.getState() + ipcMain.on 'execute-javascript-in-dev-tools', (event, code) -> event.sender.devToolsWebContents?.executeJavaScript(code) From 8307fb84265509bcaaed99f9c6b8b452f9a56bfb Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 26 Feb 2016 16:39:42 -0800 Subject: [PATCH 131/198] Change logic for `platformSupportsUpdates --- src/auto-update-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 14ec44c29..c1446ed5d 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -42,7 +42,7 @@ export default class AutoUpdateManager { } platformSupportsUpdates () { - return this.getReleaseChannel() == 'stable' && (this.getPlatform() === 'darwin' || this.getPlatform() === 'win32') + return this.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' } onDidBeginCheckingForUpdate (callback) { From d831ec73ed6adeccc1b355f96167daf1630031af Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:42:25 -0800 Subject: [PATCH 132/198] Fix platformSupportsUpdates() specs --- spec/auto-update-manager-spec.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index e12f85c5b..2b65d09e5 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -65,32 +65,24 @@ describe('AutoUpdateManager (renderer)', () => { }) describe('::platformSupportsUpdates', () => { - let platform, releaseChannel + let state, releaseChannel it('returns true on OS X and Windows, when in stable', () => { - spyOn(autoUpdateManager, 'getPlatform').andCallFake(() => platform) + spyOn(autoUpdateManager, 'getState').andCallFake(() => state) spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) - platform = 'win32' + state = 'idle' releaseChannel = 'stable' expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) - platform = 'win32' + state = 'idle' releaseChannel = 'dev' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - platform = 'darwin' - releaseChannel = 'stable' - expect(autoUpdateManager.platformSupportsUpdates()).toBe(true) - - platform = 'darwin' - releaseChannel = 'dev' - expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - - platform = 'linux' + state = 'unsupported' releaseChannel = 'stable' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) - platform = 'linux' + state = 'unsupported' releaseChannel = 'dev' expect(autoUpdateManager.platformSupportsUpdates()).toBe(false) }) From 8eb1326d4c7170768cb0d71908803220c28aab0f Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:42:37 -0800 Subject: [PATCH 133/198] Add some TODOs --- src/application-delegate.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index a853cd8ea..dff496fdd 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -174,6 +174,9 @@ class ApplicationDelegate onUpdateAvailable: (callback) -> outerCallback = (event, message, detail) -> + # TODO: Yes, this is strange that `onUpdateAvailable` is listening for + # `did-begin-downloading-update`. We currently have no mechanism to know + # if there is an update, so begin of downloading is a good proxy. callback(detail) if message is 'did-begin-downloading-update' ipcRenderer.on('message', outerCallback) @@ -193,6 +196,7 @@ class ApplicationDelegate onDidCompleteDownloadingUpdate: (callback) -> outerCallback = (event, message, detail) -> + # TODO: We could rename this event to `did-complete-downloading-update` callback(detail) if message is 'update-available' ipcRenderer.on('message', outerCallback) From 75aca7ee0c9e1a61349114f5d3d78b676d58b2b2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 29 Feb 2016 16:47:53 -0800 Subject: [PATCH 134/198] Add a deprecation TODO --- src/atom-environment.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2122abbbe..df9130453 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -884,6 +884,7 @@ class AtomEnvironment extends Model detail: error.message dismissable: true + # TODO: We should deprecate the update events here, and use `atom.autoUpdater` instead onUpdateAvailable: (callback) -> @emitter.on 'update-available', callback From 58bf090724bd9b0f5917d9ce1dde2746c53e38d4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:41:38 -0800 Subject: [PATCH 135/198] Rename `dispose` -> `destroy` --- spec/auto-update-manager-spec.js | 8 ++++---- src/atom-environment.coffee | 1 + src/auto-update-manager.js | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 2b65d09e5..9c82400ef 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -14,7 +14,7 @@ describe('AutoUpdateManager (renderer)', () => { }) afterEach(() => { - autoUpdateManager.dispose() + autoUpdateManager.destroy() }) describe('::onDidBeginCheckingForUpdate', () => { @@ -88,8 +88,8 @@ describe('AutoUpdateManager (renderer)', () => { }) }) - describe('::dispose', () => { - it('subscribes to "update-not-available" event', () => { + describe('::destroy', () => { + it('unsubscribes from all events', () => { const spy = jasmine.createSpy('spy') const doneIndicator = jasmine.createSpy('spy') atom.applicationDelegate.onUpdateNotAvailable(doneIndicator) @@ -97,7 +97,7 @@ describe('AutoUpdateManager (renderer)', () => { autoUpdateManager.onDidBeginDownloadingUpdate(spy) autoUpdateManager.onDidCompleteDownloadingUpdate(spy) autoUpdateManager.onUpdateNotAvailable(spy) - autoUpdateManager.dispose() + autoUpdateManager.destroy() electronAutoUpdater.emit('checking-for-update') electronAutoUpdater.emit('update-available') electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3') diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index df9130453..4fecd01aa 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -338,6 +338,7 @@ class AtomEnvironment extends Model @commands.clear() @stylesElement.remove() @config.unobserveUserConfig() + @autoUpdater.destroy() @uninstallWindowEventHandler() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index c1446ed5d..3b2d27439 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -24,7 +24,7 @@ export default class AutoUpdateManager { ) } - dispose () { + destroy () { this.subscriptions.dispose() this.emitter.dispose() } From 81c07a2af9322e32b758c088663887b233db1808 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:42:53 -0800 Subject: [PATCH 136/198] :art: Cleanup spec desc --- spec/auto-update-manager-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 9c82400ef..7436be291 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -66,7 +66,7 @@ describe('AutoUpdateManager (renderer)', () => { describe('::platformSupportsUpdates', () => { let state, releaseChannel - it('returns true on OS X and Windows, when in stable', () => { + it('returns true on OS X and Windows when in stable', () => { spyOn(autoUpdateManager, 'getState').andCallFake(() => state) spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) From 02368a9fc680f658dacbd0b06e1531959dd6c340 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:51:45 -0800 Subject: [PATCH 137/198] Add `atom.getReleaseChannel` --- spec/atom-environment-spec.coffee | 15 +++++++++++++++ src/atom-environment.coffee | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 1f8eb08e7..6685d4060 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -328,3 +328,18 @@ describe "AtomEnvironment", -> runs -> {releaseVersion} = updateAvailableHandler.mostRecentCall.args[0] expect(releaseVersion).toBe 'version' + + describe "::getReleaseChannel()", -> + [version] = [] + beforeEach -> + spyOn(atom, 'getVersion').andCallFake -> version + + it "returns the correct channel based on the version number", -> + version = '1.5.6' + expect(atom.getReleaseChannel()).toBe 'stable' + + version = '1.5.0-beta10' + expect(atom.getReleaseChannel()).toBe 'beta' + + version = '1.7.0-dev-5340c91' + expect(atom.getReleaseChannel()).toBe 'dev' diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 4fecd01aa..14d3f78c9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -417,6 +417,16 @@ class AtomEnvironment extends Model getVersion: -> @appVersion ?= @getLoadSettings().appVersion + # Returns the release channel as a {String}. Will return one of `'dev', 'beta', 'stable'` + getReleaseChannel: -> + version = @getVersion() + if version.indexOf('beta') > -1 + 'beta' + else if version.indexOf('dev') > -1 + 'dev' + else + 'stable' + # Public: Returns a {Boolean} that is `true` if the current version is an official release. isReleasedVersion: -> not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix From 0b36d85bdac5679097f48cefa3f0bd44fe367b4c Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:51:57 -0800 Subject: [PATCH 138/198] :art: Remove whitespace --- src/atom-environment.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 14d3f78c9..06296fbaf 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -846,7 +846,6 @@ class AtomEnvironment extends Model @applicationDelegate.setTemporaryWindowState(state) savePromise.catch(reject).then(resolve) - loadState: -> if @enablePersistence if stateKey = @getStateKey(@getLoadSettings().initialPaths) From 4d11ff25d0953c1debdacaef1b05f9a736b164f4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:59:02 -0800 Subject: [PATCH 139/198] Use atom.getReleaseChannel() --- spec/auto-update-manager-spec.js | 2 +- src/auto-update-manager.js | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 7436be291..6f7dbbb1a 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -68,7 +68,7 @@ describe('AutoUpdateManager (renderer)', () => { let state, releaseChannel it('returns true on OS X and Windows when in stable', () => { spyOn(autoUpdateManager, 'getState').andCallFake(() => state) - spyOn(autoUpdateManager, 'getReleaseChannel').andCallFake(() => releaseChannel) + spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel) state = 'idle' releaseChannel = 'stable' diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 3b2d27439..4a184faf8 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -42,7 +42,7 @@ export default class AutoUpdateManager { } platformSupportsUpdates () { - return this.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' + return atom.getReleaseChannel() !== 'dev' && this.getState() !== 'unsupported' } onDidBeginCheckingForUpdate (callback) { @@ -70,15 +70,4 @@ export default class AutoUpdateManager { getPlatform () { return process.platform } - - // TODO: We should move this into atom env or something. - getReleaseChannel () { - let version = atom.getVersion() - if (version.indexOf('beta') > -1) { - return 'beta' - } else if (version.indexOf('dev') > -1) { - return 'dev' - } - return 'stable' - } } From 492e89c8fff3d9fa96634a8510eab1f3222dfe42 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 14:59:41 -0800 Subject: [PATCH 140/198] Move raw ipc calls into the applicationDelegate --- src/application-delegate.coffee | 9 +++++++++ src/auto-update-manager.js | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/application-delegate.coffee b/src/application-delegate.coffee index dff496fdd..3aff9e457 100644 --- a/src/application-delegate.coffee +++ b/src/application-delegate.coffee @@ -235,3 +235,12 @@ class ApplicationDelegate disablePinchToZoom: -> webFrame.setZoomLevelLimits(1, 1) + + checkForUpdate: -> + ipcRenderer.send('check-for-update') + + restartAndInstallUpdate: -> + ipcRenderer.send('install-update') + + getAutoUpdateManagerState: -> + ipcRenderer.sendSync('get-auto-update-manager-state') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 4a184faf8..01ead9bbe 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -5,6 +5,7 @@ import {ipcRenderer} from 'electron' export default class AutoUpdateManager { constructor ({applicationDelegate}) { + this.applicationDelegate = applicationDelegate this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() @@ -30,15 +31,15 @@ export default class AutoUpdateManager { } checkForUpdate () { - ipcRenderer.send('check-for-update') + this.applicationDelegate.checkForUpdate() } restartAndInstallUpdate () { - ipcRenderer.send('install-update') + this.applicationDelegate.restartAndInstallUpdate() } getState () { - return ipcRenderer.sendSync('get-auto-update-manager-state') + return this.applicationDelegate.getAutoUpdateManagerState() } platformSupportsUpdates () { From f884c987e29acef83a40164b681950a7fd8bf047 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 15:12:38 -0800 Subject: [PATCH 141/198] Remove unnecessary import --- src/auto-update-manager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 01ead9bbe..62cc03f85 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -1,7 +1,6 @@ 'use babel' import {Emitter, CompositeDisposable} from 'event-kit' -import {ipcRenderer} from 'electron' export default class AutoUpdateManager { constructor ({applicationDelegate}) { From f26c0c0b15231f853f82a8fc6c7e527a27154d89 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Mar 2016 18:03:50 -0700 Subject: [PATCH 142/198] Bump timeouts as a possible workaround for flaky specs --- spec/async-spec-helpers.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/async-spec-helpers.coffee b/spec/async-spec-helpers.coffee index 5f8e03ca3..6ed8a5a2b 100644 --- a/spec/async-spec-helpers.coffee +++ b/spec/async-spec-helpers.coffee @@ -19,7 +19,9 @@ exports.afterEach = (fn) -> waitsForPromise = (fn) -> promise = fn() - waitsFor 'spec promise to resolve', 30000, (done) -> + # This timeout is 3 minutes. We need to bump it back down once we fix backgrounding + # of the renderer process on CI. See https://github.com/atom/electron/issues/4317 + waitsFor 'spec promise to resolve', 3 * 60 * 1000, (done) -> promise.then( done, (error) -> From 47a348f557bce3c6518e572d9ad45f181c3ffde9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 1 Mar 2016 18:22:43 -0700 Subject: [PATCH 143/198] Filter out non-directory project paths when deserializing Project Closes #10628 --- spec/project-spec.coffee | 8 ++++++++ src/project.coffee | 1 + 2 files changed, 9 insertions(+) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9d42f9a7e..70022426a 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -21,6 +21,14 @@ describe "Project", -> afterEach -> deserializedProject?.destroy() + it "does not deserialize paths to non directories", -> + deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + state = atom.project.serialize() + state.paths.push('/directory/that/does/not/exist') + state.paths.push(path.join(__dirname, 'fixtures', 'sample.js')) + deserializedProject.deserialize(state, atom.deserializers) + expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths()) + it "does not include unretained buffers in the serialized state", -> waitsForPromise -> atom.project.bufferForPath('a') diff --git a/src/project.coffee b/src/project.coffee index 008d81e3e..24a634cb1 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -56,6 +56,7 @@ class Project extends Model deserialize: (state, deserializerManager) -> state.paths = [state.path] if state.path? # backward compatibility + state.paths = state.paths.filter (directoryPath) -> fs.isDirectorySync(directoryPath) @buffers = _.compact state.buffers.map (bufferState) -> # Check that buffer's file path is accessible From ea2d72f314e24f1aee8c336064986520e1877895 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 1 Mar 2016 18:13:53 -0800 Subject: [PATCH 144/198] :arrow_up: about@1.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ff3df275..005b066ee 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "one-light-syntax": "1.2.0", "solarized-dark-syntax": "1.0.0", "solarized-light-syntax": "1.0.0", - "about": "1.3.1", + "about": "1.4.0", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.0", From f6908f80cdfa8699c98d808d8c22fc4e2dd4b077 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Mar 2016 15:38:29 -0700 Subject: [PATCH 145/198] Rename watchProjectPath to watchProjectPaths --- src/atom-environment.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 06296fbaf..661201125 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -676,7 +676,7 @@ class AtomEnvironment extends Model @document.body.appendChild(@views.getView(@workspace)) @backgroundStylesheet?.remove() - @watchProjectPath() + @watchProjectPaths() @packages.activate() @keymaps.loadUserKeymap() @@ -808,7 +808,7 @@ class AtomEnvironment extends Model @themes.load() # Notify the browser project of the window's current project path - watchProjectPath: -> + watchProjectPaths: -> @disposables.add @project.onDidChangePaths => @applicationDelegate.setRepresentedDirectoryPaths(@project.getPaths()) From 51a2609bc90a937507b7e226dad67a3a7ea6b8c6 Mon Sep 17 00:00:00 2001 From: Jason Koenig Date: Sat, 30 Jan 2016 23:28:43 -0800 Subject: [PATCH 146/198] Properly restore initialPaths so project paths are not loaded as files If project folders no longer exist or are not mounted, then Atom would open them as buffers, which were empty because the corresponding file did not exist. This threads the right state through the startup sequence so that "initialPaths", which actually are project roots, is restored correctly. --- src/browser/atom-application.coffee | 16 ++++++++-------- src/browser/atom-window.coffee | 13 ++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index fdfea5474..b2d66af87 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -85,16 +85,16 @@ class AtomApplication else @loadState(options) or @openPath(options) - openWithOptions: ({pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> + openWithOptions: ({initialPaths, pathsToOpen, executedFrom, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, profileStartup, timeout, clearWindowState, addToLastWindow}) -> if test @runTests({headless: true, devMode, @resourcePath, executedFrom, pathsToOpen, logFile, timeout}) else if pathsToOpen.length > 0 - @openPaths({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPaths({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) else if urlsToOpen.length > 0 @openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen else # Always open a editor window if this is the first instance of Atom. - @openPath({pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) + @openPath({initialPaths, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, clearWindowState, addToLastWindow}) # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> @@ -418,8 +418,8 @@ class AtomApplication # :profileStartup - Boolean to control creating a profile of the startup time. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> - @openPaths({pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) + openPath: ({initialPaths, pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow} = {}) -> + @openPaths({initialPaths, pathsToOpen: [pathToOpen], pidToKillWhenClosed, newWindow, devMode, safeMode, profileStartup, window, clearWindowState, addToLastWindow}) # Public: Opens multiple paths, in existing windows if possible. # @@ -432,7 +432,7 @@ class AtomApplication # :windowDimensions - Object with height and width keys. # :window - {AtomWindow} to open file paths in. # :addToLastWindow - Boolean of whether this should be opened in last focused window. - openPaths: ({pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> + openPaths: ({initialPaths, pathsToOpen, executedFrom, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions, profileStartup, window, clearWindowState, addToLastWindow}={}) -> devMode = Boolean(devMode) safeMode = Boolean(safeMode) clearWindowState = Boolean(clearWindowState) @@ -469,7 +469,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') resourcePath ?= @resourcePath windowDimensions ?= @getDimensionsForNewWindow() - openedWindow = new AtomWindow({locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) + openedWindow = new AtomWindow({initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState}) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow @@ -512,7 +512,7 @@ class AtomApplication if (states = @storageFolder.load('application.json'))?.length > 0 for state in states @openWithOptions(_.extend(options, { - pathsToOpen: state.initialPaths + initialPaths: state.initialPaths urlsToOpen: [] devMode: @devMode safeMode: @safeMode diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 634242e0d..24730c944 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -17,7 +17,7 @@ class AtomWindow isSpec: null constructor: (settings={}) -> - {@resourcePath, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings + {@resourcePath, initialPaths, pathToOpen, locationsToOpen, @isSpec, @headless, @safeMode, @devMode} = settings locationsToOpen ?= [{pathToOpen}] if pathToOpen locationsToOpen ?= [] @@ -47,20 +47,15 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.clearWindowState ?= false + loadSettings.initialPaths ?= [] + loadSettings.initialPaths.sort() # Only send to the first non-spec window created if @constructor.includeShellLoadTime and not @isSpec @constructor.includeShellLoadTime = false loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime - loadSettings.initialPaths = - for {pathToOpen} in locationsToOpen when pathToOpen - if fs.statSyncNoException(pathToOpen).isFile?() - path.dirname(pathToOpen) - else - pathToOpen - - loadSettings.initialPaths.sort() + @browserWindow.loadSettings = loadSettings @browserWindow.once 'window:loaded', => @emit 'window:loaded' From 64ed1a0e43aa30f03504755739b8c10d6f6fbbc8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 2 Mar 2016 17:00:27 -0700 Subject: [PATCH 147/198] Populate initialPaths based on pathsToOpen if not specified This ensures that we deserialize projects correctly when specifying command line arguments. --- src/browser/atom-window.coffee | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 24730c944..33c64da7d 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -47,7 +47,13 @@ class AtomWindow loadSettings.safeMode ?= false loadSettings.atomHome = process.env.ATOM_HOME loadSettings.clearWindowState ?= false - loadSettings.initialPaths ?= [] + loadSettings.initialPaths ?= + for {pathToOpen} in locationsToOpen when pathToOpen + if fs.statSyncNoException(pathToOpen).isFile?() + path.dirname(pathToOpen) + else + pathToOpen + loadSettings.initialPaths.sort() # Only send to the first non-spec window created From 2bdf4be904ceecbb974d13930285fa4eb6a87db4 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 2 Mar 2016 16:56:06 -0800 Subject: [PATCH 148/198] :arrow_up: atom-package-manager Signed-off-by: Katrina Uychaco --- apm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apm/package.json b/apm/package.json index 2e6b0b8ea..4b599bc39 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.6.0" + "atom-package-manager": "1.7.1" } } From db20cecfc08b262aad2ca01e98d7e5943007d2ec Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 09:59:45 -0500 Subject: [PATCH 149/198] s/ignoreInvisibles/showInvisibles --- spec/text-editor-spec.coffee | 4 ++-- src/text-editor.coffee | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index c75084fd5..637244964 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5829,11 +5829,11 @@ describe "TextEditor", -> rangeIsReversed: false } - describe "when the editor is constructed with the ignoreInvisibles option set to true", -> + describe "when the editor is constructed with the showInvisibles option set to false", -> beforeEach -> atom.workspace.destroyActivePane() waitsForPromise -> - atom.workspace.open('sample.js', ignoreInvisibles: true).then (o) -> editor = o + atom.workspace.open('sample.js', showInvisibles: false).then (o) -> editor = o it "ignores invisibles even if editor.showInvisibles is true", -> atom.config.set('editor.showInvisibles', true) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6ce96088b..6eeb2cff5 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, ignoreInvisibles, @autoHeight, @ignoreScrollPastEnd + @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @ignoreScrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -123,9 +123,11 @@ class TextEditor extends Model @ignoreScrollPastEnd ?= false @hasTerminatedPendingState = false + showInvisibles ?= true + buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini or ignoreInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or !showInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From 98c8a08ac3837dc59c00e77a913af5f1358f6a14 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:06:15 -0500 Subject: [PATCH 150/198] s/ignoreScrollPastEnd/scrollPastEnd --- spec/text-editor-presenter-spec.coffee | 4 ++-- src/text-editor-component.coffee | 4 ++-- src/text-editor-element.coffee | 6 +++--- src/text-editor-presenter.coffee | 6 ++++-- src/text-editor.coffee | 8 ++++---- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 62d1e4747..f8117af09 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -647,8 +647,8 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight - it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but ignoreScrollPastEnd is true", -> - presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, ignoreScrollPastEnd: true) + it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but the presenter is created with scrollPastEnd as false", -> + presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, scrollPastEnd: false) expectStateUpdate presenter, -> presenter.setScrollTop(300) expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 5a4097fc5..9b091100d 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -43,7 +43,7 @@ class TextEditorComponent @assert domNode?, "TextEditorComponent::domNode was set to null." @domNodeValue = domNode - constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, ignoreScrollPastEnd}) -> + constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize, @views, @themes, @config, @workspace, @assert, @grammars, scrollPastEnd}) -> @tileSize = tileSize if tileSize? @disposables = new CompositeDisposable @@ -61,7 +61,7 @@ class TextEditorComponent stoppedScrollingDelay: 200 config: @config lineTopIndex: lineTopIndex - ignoreScrollPastEnd: ignoreScrollPastEnd + scrollPastEnd: scrollPastEnd @presenter.onDidUpdateState(@requestUpdate) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 02f688e2c..df13f2a15 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -17,7 +17,7 @@ class TextEditorElement extends HTMLElement focusOnAttach: false hasTiledRendering: true logicalDisplayBuffer: true - ignoreScrollPastEnd: false + scrollPastEnd: true createdCallback: -> # Use globals when the following instance variables aren't set. @@ -90,7 +90,7 @@ class TextEditorElement extends HTMLElement @subscriptions.add @component.onDidChangeScrollLeft => @emitter.emit("did-change-scroll-left", arguments...) - initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @ignoreScrollPastEnd = false) -> + initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @scrollPastEnd = true) -> throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views? throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config? throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes? @@ -147,7 +147,7 @@ class TextEditorElement extends HTMLElement workspace: @workspace assert: @assert grammars: @grammars - ignoreScrollPastEnd: @ignoreScrollPastEnd + scrollPastEnd: @scrollPastEnd ) @rootElement.appendChild(@component.getDomNode()) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 0db175c2b..1fa662c59 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -13,7 +13,7 @@ class TextEditorPresenter minimumReflowInterval: 200 constructor: (params) -> - {@model, @config, @lineTopIndex, @ignoreScrollPastEnd} = params + {@model, @config, @lineTopIndex, scrollPastEnd} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @tileSize} = params {@contentFrameWidth} = params @@ -42,6 +42,8 @@ class TextEditorPresenter @startReflowing() if @continuousReflow @updating = false + @scrollPastEndOverride = scrollPastEnd || true + setLinesYardstick: (@linesYardstick) -> getLinesYardstick: -> @linesYardstick @@ -661,7 +663,7 @@ class TextEditorPresenter return unless @contentHeight? and @clientHeight? contentHeight = @contentHeight - if @scrollPastEnd and not @ignoreScrollPastEnd + if @scrollPastEnd and @scrollPastEndOverride extraScrollHeight = @clientHeight - (@lineHeight * 3) contentHeight += extraScrollHeight if extraScrollHeight > 0 scrollHeight = Math.max(contentHeight, @height) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 6eeb2cff5..54ea283e9 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @ignoreScrollPastEnd + @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -120,7 +120,7 @@ class TextEditor extends Model @cursorsByMarkerId = new Map @selections = [] @autoHeight ?= true - @ignoreScrollPastEnd ?= false + @scrollPastEnd ?= true @hasTerminatedPendingState = false showInvisibles ?= true @@ -3156,7 +3156,7 @@ class TextEditor extends Model # Get the Element for the editor. getElement: -> - @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @ignoreScrollPastEnd) + @editorElement ?= new TextEditorElement().initialize(this, atom, @autoHeight, @scrollPastEnd) # Essential: Retrieves the greyed out placeholder of a mini editor. # @@ -3232,7 +3232,7 @@ class TextEditor extends Model setFirstVisibleScreenRow: (screenRow, fromView) -> unless fromView maxScreenRow = @getScreenLineCount() - 1 - unless @config.get('editor.scrollPastEnd') and not @ignoreScrollPastEnd + unless @config.get('editor.scrollPastEnd') and @scrollPastEnd height = @displayBuffer.getHeight() lineHeightInPixels = @displayBuffer.getLineHeightInPixels() if height? and lineHeightInPixels? From 928205a44ae7b18d7257a6853e34e33d5ed562c9 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:09:18 -0500 Subject: [PATCH 151/198] s/grammarName/grammar --- spec/text-editor-spec.coffee | 4 ++-- src/text-editor.coffee | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 637244964..9d2a2a58c 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -5840,14 +5840,14 @@ describe "TextEditor", -> invisibles = editor.tokenizedLineForScreenRow(0).invisibles expect(invisibles).toBe(null) - describe "when the editor is constructed with the grammarName option set", -> + describe "when the editor is constructed with the grammar option set", -> beforeEach -> atom.workspace.destroyActivePane() waitsForPromise -> atom.packages.activatePackage('language-coffee-script') waitsForPromise -> - atom.workspace.open('sample.js', grammarName: 'source.coffee').then (o) -> editor = o + atom.workspace.open('sample.js', grammar: atom.grammars.grammarForScopeName('source.coffee')).then (o) -> editor = o it "sets the grammar", -> expect(editor.getGrammar().name).toBe 'CoffeeScript' diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 54ea283e9..b981dcbb2 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -100,7 +100,7 @@ class TextEditor extends Model softWrapped, @displayBuffer, @selectionsMarkerLayer, buffer, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode, @config, @notificationManager, @packageManager, @clipboard, @viewRegistry, @grammarRegistry, - @project, @assert, @applicationDelegate, grammarName, showInvisibles, @autoHeight, @scrollPastEnd + @project, @assert, @applicationDelegate, grammar, showInvisibles, @autoHeight, @scrollPastEnd } = params throw new Error("Must pass a config parameter when constructing TextEditors") unless @config? @@ -156,8 +156,8 @@ class TextEditor extends Model priority: 0 visible: lineNumberGutterVisible - if grammarName? - @setGrammar(@grammarRegistry.grammarForScopeName(grammarName)) + if grammar? + @setGrammar(grammar) serialize: -> deserializer: 'TextEditor' From 10acfd057fbf994fe2ddd5dd12d66b7dba5cee94 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:13:26 -0500 Subject: [PATCH 152/198] Err, yeah, we care about undefined, not false. --- src/text-editor-presenter.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 1fa662c59..ef1b403c3 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -42,7 +42,7 @@ class TextEditorPresenter @startReflowing() if @continuousReflow @updating = false - @scrollPastEndOverride = scrollPastEnd || true + @scrollPastEndOverride = scrollPastEnd ? true setLinesYardstick: (@linesYardstick) -> From dba1fbd40845304960b447bf0fc3c15a3ad2ef71 Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 3 Mar 2016 10:15:13 -0500 Subject: [PATCH 153/198] De-lint. --- src/text-editor.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index b981dcbb2..d5937d307 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -127,7 +127,7 @@ class TextEditor extends Model buffer ?= new TextBuffer @displayBuffer ?= new DisplayBuffer({ - buffer, tabLength, softWrapped, ignoreInvisibles: @mini or !showInvisibles, largeFileMode, + buffer, tabLength, softWrapped, ignoreInvisibles: @mini or not showInvisibles, largeFileMode, @config, @assert, @grammarRegistry, @packageManager }) @buffer = @displayBuffer.buffer From a218582aeb3186d4237bd3a4341e5b621b5d2c3b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Mar 2016 18:02:08 -0700 Subject: [PATCH 154/198] =?UTF-8?q?Supply=20pathsToOpen=20in=20case=20ther?= =?UTF-8?q?e=E2=80=99s=20no=20window=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/atom-application.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index b2d66af87..230e1bb9f 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -513,6 +513,7 @@ class AtomApplication for state in states @openWithOptions(_.extend(options, { initialPaths: state.initialPaths + pathsToOpen: state.initialPaths.filter (directoryPath) -> fs.isDirectorySync(directoryPath) urlsToOpen: [] devMode: @devMode safeMode: @safeMode From e620232d355b81afcf2c9607314c3600198bab0b Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 3 Mar 2016 17:46:02 -0800 Subject: [PATCH 155/198] Add new item before destroying pending item Fixes atom/tabs#278 Signed-off-by: Michelle Tilley --- spec/pane-spec.coffee | 30 +++++++++++++++++++++++++++--- src/pane.coffee | 4 ++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 888ccf6e2..873749a19 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -132,13 +132,37 @@ describe "Pane", -> expect(-> pane.addItem('foo')).toThrow() expect(-> pane.addItem(1)).toThrow() - it "destroys any existing pending item if the new item is pending", -> + it "destroys any existing pending item", -> + pane = new Pane(paneParams(items: [])) + itemA = new Item("A") + itemB = new Item("B") + itemC = new Item("C") + pane.addItem(itemA, undefined, false, false) + pane.addItem(itemB, undefined, false, true) + pane.addItem(itemC, undefined, false, false) + expect(itemB.isDestroyed()).toBe true + + it "adds the new item before destroying any existing pending item", -> + eventOrder = [] + pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") pane.addItem(itemA, undefined, false, true) - pane.addItem(itemB, undefined, false, true) - expect(itemA.isDestroyed()).toBe true + + pane.onDidAddItem ({item}) -> + eventOrder.push("add") if item is itemB + + pane.onDidRemoveItem ({item}) -> + eventOrder.push("remove") if item is itemA + + pane.addItem(itemB) + + waitsFor -> + eventOrder.length is 2 + + runs -> + expect(eventOrder).toEqual ["add", "remove"] describe "::activateItem(item)", -> pane = null diff --git a/src/pane.coffee b/src/pane.coffee index b944f763c..70f8b4bab 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -433,11 +433,11 @@ class Pane extends Model @subscriptionsPerItem.set item, itemSubscriptions @items.splice(index, 0, item) - pendingItem = @getPendingItem() - @destroyItem(pendingItem) if pendingItem? + lastPendingItem = @getPendingItem() @setPendingItem(item) if pending @emitter.emit 'did-add-item', {item, index, moved} + @destroyItem(lastPendingItem) if lastPendingItem? @setActiveItem(item) unless @getActiveItem()? item From 4d9e2b865b1f136bd1400eb3c67cb98dfc7571f2 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Thu, 3 Mar 2016 20:03:44 -0800 Subject: [PATCH 156/198] Ensure atom.cmd --wait correctly waits in Windows cmd & powershell --- resources/win/atom.cmd | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/resources/win/atom.cmd b/resources/win/atom.cmd index 8d5e6f97a..a1af5cd53 100644 --- a/resources/win/atom.cmd +++ b/resources/win/atom.cmd @@ -22,30 +22,13 @@ FOR %%a IN (%*) DO ( ) ) -rem Getting the process ID in cmd of the current cmd process: http://superuser.com/questions/881789/identify-and-kill-batch-script-started-before -set T=%TEMP%\atomCmdProcessId-%time::=%.tmp -wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") get ParentProcessId /value | find "ParentProcessId" >%T% -set /P A=<%T% -set PID=%A:~16% -del %T% - IF "%EXPECT_OUTPUT%"=="YES" ( SET ELECTRON_ENABLE_LOGGING=YES IF "%WAIT%"=="YES" ( - "%~dp0\..\..\atom.exe" --pid=%PID% %* - rem If the wait flag is set, don't exit this process until Atom tells it to. - goto waitLoop + powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid %* ; wait-event ) ELSE ( "%~dp0\..\..\atom.exe" %* ) ) ELSE ( "%~dp0\..\app\apm\bin\node.exe" "%~dp0\atom.js" %* ) - -goto end - -:waitLoop - sleep 1 - goto waitLoop - -:end From 1043dc083982c63a23d8d13f7653e34bd4453c50 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 3 Mar 2016 22:43:19 -0800 Subject: [PATCH 157/198] :arrow_up: bracket-matcher@0.81.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8907f16b9..f4c6475be 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "autosave": "0.23.1", "background-tips": "0.26.0", "bookmarks": "0.38.2", - "bracket-matcher": "0.80.1", + "bracket-matcher": "0.81.0", "command-palette": "0.38.0", "deprecation-cop": "0.54.1", "dev-live-reload": "0.47.0", From 7176da7614fb3b457497e639908795948df5dc64 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 15:17:10 +0100 Subject: [PATCH 158/198] Use structured cloning --- src/state-store.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/state-store.js b/src/state-store.js index 13c9a0462..a2d3b476b 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -24,16 +24,13 @@ class StateStore { } save (key, value) { - // Serialize values using JSON.stringify, as it seems way faster than IndexedDB structured clone. - // (Ref.: https://bugs.chromium.org/p/chromium/issues/detail?id=536620) - let jsonValue = JSON.stringify(value) return new Promise((resolve, reject) => { this.dbPromise.then(db => { if (db == null) resolve() var request = db.transaction(['states'], 'readwrite') .objectStore('states') - .put({value: jsonValue, storedAt: new Date().toString(), isJSON: true}, key) + .put({value: value, storedAt: new Date().toString()}, key) request.onsuccess = resolve request.onerror = reject @@ -52,9 +49,8 @@ class StateStore { request.onsuccess = (event) => { let result = event.target.result - if (result) { - // TODO: remove this when state will be serialized only via JSON. - resolve(result.isJSON ? JSON.parse(result.value) : result.value) + if (result && !result.isJSON) { + resolve(result.value) } else { resolve(null) } From 011fe380e9727a0f0d49230e15da25fa506835a3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 16:57:02 +0100 Subject: [PATCH 159/198] Pass markerLayer: false to buffer.serialize when not quitting --- spec/project-spec.coffee | 25 +++++++++++++++++++++---- src/project.coffee | 8 ++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9d42f9a7e..cecfe4bd4 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -29,7 +29,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -39,7 +39,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -56,7 +56,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -70,9 +70,26 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize(), atom.deserializers) + deserializedProject.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 + it "serializes marker / marker only if Atom is quitting", -> + waitsForPromise -> + atom.workspace.open('a') + + runs -> + bufferA = atom.project.getBuffers()[0] + layerA = bufferA.addMarkerLayer(maintainHistory: true) + markerA = layerA.markPosition([0, 3]) + + notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + notQuittingProject.deserialize(atom.project.serialize({isQuitting: false})) + expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).toBeUndefined() + + quittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) + quittingProject.deserialize(atom.project.serialize({isQuitting: true})) + expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).not.toBeUndefined() + describe "when an editor is saved and the project has no path", -> it "sets the project's path to the saved file's parent directory", -> tempFile = temp.openSync().path diff --git a/src/project.coffee b/src/project.coffee index 008d81e3e..538c4acb4 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -54,7 +54,7 @@ class Project extends Model Section: Serialization ### - deserialize: (state, deserializerManager) -> + deserialize: (state) -> state.paths = [state.path] if state.path? # backward compatibility @buffers = _.compact state.buffers.map (bufferState) -> @@ -65,15 +65,15 @@ class Project extends Model fs.closeSync(fs.openSync(bufferState.filePath, 'r')) catch error return unless error.code is 'ENOENT' - deserializerManager.deserialize(bufferState) + TextBuffer.deserialize(bufferState) @subscribeToBuffer(buffer) for buffer in @buffers @setPaths(state.paths) - serialize: -> + serialize: (options) -> deserializer: 'Project' paths: @getPaths() - buffers: _.compact(@buffers.map (buffer) -> buffer.serialize() if buffer.isRetained()) + buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isQuitting is true}) if buffer.isRetained()) ### Section: Event Subscription From 311cde36c963ee85c9bc011baf38061cd7b01212 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:06:53 +0100 Subject: [PATCH 160/198] Call saveState(isQuitting: true) on beforeUnload --- spec/window-event-handler-spec.coffee | 5 +++++ src/window-event-handler.coffee | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index bb7e1665b..b9f102c3f 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,6 +55,11 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') + it "saves AtomEnvironment's state with the {isQuitting: true} option", -> + spyOn(atom, 'saveState') + window.dispatchEvent(new CustomEvent('beforeunload')) + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: true}) + describe "when pane items are modified", -> editor = null beforeEach -> diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index ce08344c6..10bebbf1e 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,7 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState() + @atomEnvironment.saveState({isQuitting: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 0fdc19098936aed7597425352f19e8a1726ae046 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:08:32 +0100 Subject: [PATCH 161/198] Use isQuitting: false when saving state on key/mouse down --- spec/atom-environment-spec.coffee | 4 ++-- src/atom-environment.coffee | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 1f8eb08e7..d8d700dd7 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -185,12 +185,12 @@ describe "AtomEnvironment", -> keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalled() + expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 0ee12fe93..983148ca4 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -225,7 +225,7 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState()), @saveStateDebounceInterval) + debouncedSaveState = _.debounce((=> @saveState({isQuitting: false})), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => From 3bdb83f97ec1018749695313e3c8a38da1f6a5af Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:11:34 +0100 Subject: [PATCH 162/198] Pass saveState options to project.serialize --- src/atom-environment.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 983148ca4..748e73ef9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -670,9 +670,9 @@ class AtomEnvironment extends Model @openInitialEmptyEditorIfNecessary() - serialize: -> + serialize: (options) -> version: @constructor.version - project: @project.serialize() + project: @project.serialize(options) workspace: @workspace.serialize() packageStates: @packages.serialize() grammars: {grammarOverridesByPath: @grammars.grammarOverridesByPath} @@ -817,9 +817,12 @@ class AtomEnvironment extends Model @blobStore.save() - saveState: -> + saveState: (options) -> return Promise.resolve() unless @enablePersistence + options ?= {} + options.isQuitting ?= false + new Promise (resolve, reject) => window.requestIdleCallback => state = @serialize() From 41c293ef9d79e5ae1d1268e8cf932d59c5664f9d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:23:19 +0100 Subject: [PATCH 163/198] Always pass {isQuitting} in tests --- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/project-spec.coffee | 8 ++++---- spec/workspace-spec.coffee | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index fa5b0d711..80ba2ebb1 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -541,7 +541,7 @@ describe('GitRepositoryAsync', () => { await atom.workspace.open('file.txt') project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize(), atom.deserializers) + project2.deserialize(atom.project.serialize({isQuitting: true})) const repo = project2.getRepositories()[0].async waitsForPromise(() => repo.refreshStatus()) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 22c40c19a..9124f78cc 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -347,7 +347,7 @@ describe "GitRepository", -> runs -> project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize(), atom.deserializers) + project2.deserialize(atom.project.serialize({isQuitting: false})) buffer = project2.getBuffers()[0] waitsFor -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index cecfe4bd4..9a392dd62 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -29,7 +29,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -39,7 +39,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -56,7 +56,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -70,7 +70,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize()) + deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "serializes marker / marker only if Atom is quitting", -> diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 2e15431b2..214fc477f 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -22,11 +22,11 @@ describe "Workspace", -> describe "serialization", -> simulateReload = -> workspaceState = atom.workspace.serialize() - projectState = atom.project.serialize() + projectState = atom.project.serialize({isQuitting: true}) atom.workspace.destroy() atom.project.destroy() atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)}) - atom.project.deserialize(projectState, atom.deserializers) + atom.project.deserialize(projectState) atom.workspace = new Workspace({ config: atom.config, project: atom.project, packageManager: atom.packages, grammarRegistry: atom.grammars, deserializerManager: atom.deserializers, From 575065f3e92aadc944d3c749ae834bd8f22fa5c2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:37:53 +0100 Subject: [PATCH 164/198] Don't forget to pass the option during saveState --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 748e73ef9..40cbe0089 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -825,7 +825,7 @@ class AtomEnvironment extends Model new Promise (resolve, reject) => window.requestIdleCallback => - state = @serialize() + state = @serialize(options) savePromise = if storageKey = @getStateKey(@project?.getPaths()) @stateStore.save(storageKey, state) From 046cdddefe58db79c7deafdc180ce40697a6bfe4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 4 Mar 2016 17:53:14 +0100 Subject: [PATCH 165/198] :memo: --- spec/project-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 9a392dd62..0c76ad9bb 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -73,7 +73,7 @@ describe "Project", -> deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) expect(deserializedProject.getBuffers().length).toBe 0 - it "serializes marker / marker only if Atom is quitting", -> + it "serializes marker layers only if Atom is quitting", -> waitsForPromise -> atom.workspace.open('a') From d9ee94031f31abccfdda23d528f8f42ded48fbf0 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Mar 2016 13:40:16 -0700 Subject: [PATCH 166/198] Test disposal of manually-created tooltips This also prevents subsequent tests from failing due to the manual tooltip never being removed from document.body. --- spec/tooltip-manager-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/tooltip-manager-spec.coffee b/spec/tooltip-manager-spec.coffee index 264d1a3bb..d4bfc1bd6 100644 --- a/spec/tooltip-manager-spec.coffee +++ b/spec/tooltip-manager-spec.coffee @@ -29,8 +29,10 @@ describe "TooltipManager", -> expect(document.body.querySelector(".tooltip")).toHaveText("Title") it "creates a tooltip immediately if the trigger type is manual", -> - manager.add element, title: "Title", trigger: "manual" + disposable = manager.add element, title: "Title", trigger: "manual" expect(document.body.querySelector(".tooltip")).toHaveText("Title") + disposable.dispose() + expect(document.body.querySelector(".tooltip")).toBeNull() it "allows jQuery elements to be passed as the target", -> element2 = document.createElement('div') From 6cc80e05ff4a98512f0882912c446dfdb7d4027b Mon Sep 17 00:00:00 2001 From: joshaber Date: Fri, 4 Mar 2016 16:36:47 -0500 Subject: [PATCH 167/198] Default to auto height being true. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise TextEditorElements created through the tag wouldn’t have a setting and be wrong. --- src/text-editor-element.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index df13f2a15..2a9b5e262 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -18,6 +18,7 @@ class TextEditorElement extends HTMLElement hasTiledRendering: true logicalDisplayBuffer: true scrollPastEnd: true + autoHeight: true createdCallback: -> # Use globals when the following instance variables aren't set. From b4f335d9a18ceb719fa47e1c2c54a43993e6d5aa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Mar 2016 17:01:26 -0700 Subject: [PATCH 168/198] :arrow_up: tree-view --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4c6475be..6a5f47479 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.111.1", "tabs": "0.91.1", "timecop": "0.33.1", - "tree-view": "0.201.5", + "tree-view": "0.202.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 341afed0ab7e5c9fbccd69030dce6e77a4bb4e75 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Fri, 4 Mar 2016 19:40:00 -0500 Subject: [PATCH 169/198] :arrow_up: language-json@0.17.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6a5f47479..3a6d0910c 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "language-hyperlink": "0.16.0", "language-java": "0.17.0", "language-javascript": "0.110.0", - "language-json": "0.17.4", + "language-json": "0.17.5", "language-less": "0.29.0", "language-make": "0.21.0", "language-mustache": "0.13.0", From 42a696cf28f00b9cfe33107ea57e33c4e38ea3f4 Mon Sep 17 00:00:00 2001 From: Rodrigo Espinosa Curbelo Date: Sat, 5 Mar 2016 21:28:06 -0300 Subject: [PATCH 170/198] :bug: Scroll to cursor when unfold all Fixes #11066 --- src/text-editor.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d5937d307..e858d8c80 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2936,6 +2936,7 @@ class TextEditor extends Model # Extended: Unfold all existing folds. unfoldAll: -> @languageMode.unfoldAll() + @scrollToCursorPosition() # Extended: Fold all foldable lines at the given indent level. # From f88051e5e624f5519932a626d135e23dfa7449bd Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Sun, 6 Mar 2016 00:56:35 -0800 Subject: [PATCH 171/198] :arrow_up: find-and-replace (cherry picked from commit 2d5d0d21bde88fcc04b6b28ded6ad84b5a85fe39) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a6d0910c..3a36478bd 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.197.3", + "find-and-replace": "0.197.4", "fuzzy-finder": "1.0.2", "git-diff": "1.0.0", "go-to-line": "0.30.0", From 95dbbea8f536753c7952f231fe479f3cc7e74d20 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:32:19 +0100 Subject: [PATCH 172/198] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c55268dd..aa74e94f2 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.3.2", + "text-buffer": "8.4.0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From 12587073d2eca35079c860c8c42fcb9b2065f6e7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:52:16 +0100 Subject: [PATCH 173/198] :art: isQuitting -> isUnloading --- spec/atom-environment-spec.coffee | 4 ++-- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 2 +- spec/project-spec.coffee | 12 ++++++------ spec/window-event-handler-spec.coffee | 4 ++-- spec/workspace-spec.coffee | 2 +- src/atom-environment.coffee | 4 ++-- src/project.coffee | 2 +- src/window-event-handler.coffee | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 82a38942c..556312082 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -185,12 +185,12 @@ describe "AtomEnvironment", -> keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: false}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 80ba2ebb1..e7bffad3a 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -541,7 +541,7 @@ describe('GitRepositoryAsync', () => { await atom.workspace.open('file.txt') project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize({isQuitting: true})) + project2.deserialize(atom.project.serialize({isUnloading: true})) const repo = project2.getRepositories()[0].async waitsForPromise(() => repo.refreshStatus()) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 9124f78cc..3afd4da75 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -347,7 +347,7 @@ describe "GitRepository", -> runs -> project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - project2.deserialize(atom.project.serialize({isQuitting: false})) + project2.deserialize(atom.project.serialize({isUnloading: false})) buffer = project2.getBuffers()[0] waitsFor -> diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 7f13cda40..499efd017 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -37,7 +37,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -47,7 +47,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -64,7 +64,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -78,7 +78,7 @@ describe "Project", -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - deserializedProject.deserialize(atom.project.serialize({isQuitting: false})) + deserializedProject.deserialize(atom.project.serialize({isUnloading: false})) expect(deserializedProject.getBuffers().length).toBe 0 it "serializes marker layers only if Atom is quitting", -> @@ -91,11 +91,11 @@ describe "Project", -> markerA = layerA.markPosition([0, 3]) notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - notQuittingProject.deserialize(atom.project.serialize({isQuitting: false})) + notQuittingProject.deserialize(atom.project.serialize({isUnloading: false})) expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).toBeUndefined() quittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm}) - quittingProject.deserialize(atom.project.serialize({isQuitting: true})) + quittingProject.deserialize(atom.project.serialize({isUnloading: true})) expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).not.toBeUndefined() describe "when an editor is saved and the project has no path", -> diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index b9f102c3f..7f093aeff 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,10 +55,10 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') - it "saves AtomEnvironment's state with the {isQuitting: true} option", -> + it "saves AtomEnvironment's state with the {isUnloading: true} option", -> spyOn(atom, 'saveState') window.dispatchEvent(new CustomEvent('beforeunload')) - expect(atom.saveState).toHaveBeenCalledWith({isQuitting: true}) + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) describe "when pane items are modified", -> editor = null diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index bf24f25cf..38d4839b0 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -22,7 +22,7 @@ describe "Workspace", -> describe "serialization", -> simulateReload = -> workspaceState = atom.workspace.serialize() - projectState = atom.project.serialize({isQuitting: true}) + projectState = atom.project.serialize({isUnloading: true}) atom.workspace.destroy() atom.project.destroy() atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)}) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2db72270c..f546fd63d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -230,7 +230,7 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState({isQuitting: false})), @saveStateDebounceInterval) + debouncedSaveState = _.debounce((=> @saveState({isUnloading: false})), @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => @@ -835,7 +835,7 @@ class AtomEnvironment extends Model return Promise.resolve() unless @enablePersistence options ?= {} - options.isQuitting ?= false + options.isUnloading ?= false new Promise (resolve, reject) => window.requestIdleCallback => diff --git a/src/project.coffee b/src/project.coffee index d6d36e644..5f3420b08 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -74,7 +74,7 @@ class Project extends Model serialize: (options) -> deserializer: 'Project' paths: @getPaths() - buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isQuitting is true}) if buffer.isRetained()) + buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isUnloading is true}) if buffer.isRetained()) ### Section: Event Subscription diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 10bebbf1e..490b2b416 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,7 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState({isQuitting: true}) + @atomEnvironment.saveState({isUnloading: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 914015e4ebf9ba6fff1bbac816662fd46c71dc99 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 10:52:28 +0100 Subject: [PATCH 174/198] :fire: Remove default parameters --- src/atom-environment.coffee | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f546fd63d..f99ec3bcc 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -834,9 +834,6 @@ class AtomEnvironment extends Model saveState: (options) -> return Promise.resolve() unless @enablePersistence - options ?= {} - options.isUnloading ?= false - new Promise (resolve, reject) => window.requestIdleCallback => state = @serialize(options) From 4f9bc0c06ba7bc115b09a4ed0899e407bf0206bd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 11:45:54 +0100 Subject: [PATCH 175/198] :arrow_up: text-buffer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c517fedd..ff30c0d07 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "service-hub": "^0.7.0", "source-map-support": "^0.3.2", "temp": "0.8.1", - "text-buffer": "8.4.0", + "text-buffer": "8.4.1", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" From a3bed908d7ca5b58bf61c8b9c3381d7f04c62a24 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 16:58:03 +0100 Subject: [PATCH 176/198] Don't partially serialize after unloading editor window --- spec/atom-environment-spec.coffee | 11 ++++++++++- spec/window-event-handler-spec.coffee | 5 ----- src/atom-environment.coffee | 8 +++++++- src/window-event-handler.coffee | 1 - 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 556312082..8e7af40d5 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -179,7 +179,7 @@ describe "AtomEnvironment", -> atom.loadState().then (state) -> expect(state).toEqual({stuff: 'cool'}) - it "saves state on keydown and mousedown events", -> + it "saves state on keydown and mousedown events when the editor window hasn't been unloaded", -> spyOn(atom, 'saveState') keydown = new KeyboardEvent('keydown') @@ -187,11 +187,20 @@ describe "AtomEnvironment", -> advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + atom.saveState.reset() + atom.unloadEditorWindow() + mousedown = new MouseEvent('mousedown') + atom.document.dispatchEvent(mousedown) + advanceClock atom.saveStateDebounceInterval + expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> beforeEach -> diff --git a/spec/window-event-handler-spec.coffee b/spec/window-event-handler-spec.coffee index 7f093aeff..bb7e1665b 100644 --- a/spec/window-event-handler-spec.coffee +++ b/spec/window-event-handler-spec.coffee @@ -55,11 +55,6 @@ describe "WindowEventHandler", -> jasmine.unspy(TextEditor.prototype, "shouldPromptToSave") spyOn(ipcRenderer, 'send') - it "saves AtomEnvironment's state with the {isUnloading: true} option", -> - spyOn(atom, 'saveState') - window.dispatchEvent(new CustomEvent('beforeunload')) - expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) - describe "when pane items are modified", -> editor = null beforeEach -> diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f99ec3bcc..8c79d66f6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -129,6 +129,7 @@ class AtomEnvironment extends Model constructor: (params={}) -> {@blobStore, @applicationDelegate, @window, @document, configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + @unloaded = false @loadTime = null {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() @@ -230,7 +231,8 @@ class AtomEnvironment extends Model checkPortableHomeWritable() attachSaveStateListeners: -> - debouncedSaveState = _.debounce((=> @saveState({isUnloading: false})), @saveStateDebounceInterval) + saveState = => @saveState({isUnloading: false}) unless @unloaded + debouncedSaveState = _.debounce(saveState, @saveStateDebounceInterval) @document.addEventListener('mousedown', debouncedSaveState, true) @document.addEventListener('keydown', debouncedSaveState, true) @disposables.add new Disposable => @@ -696,9 +698,11 @@ class AtomEnvironment extends Model unloadEditorWindow: -> return if not @project + @saveState({isUnloading: true}) @storeWindowBackground() @packages.deactivatePackages() @saveBlobStoreSync() + @unloaded = true openInitialEmptyEditorIfNecessary: -> return unless @config.get('core.openEmptyEditorOnStart') @@ -836,6 +840,8 @@ class AtomEnvironment extends Model new Promise (resolve, reject) => window.requestIdleCallback => + return if not @project + state = @serialize(options) savePromise = if storageKey = @getStateKey(@project?.getPaths()) diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 490b2b416..6c338320d 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -143,7 +143,6 @@ class WindowEventHandler @reloadRequested = false @atomEnvironment.storeWindowDimensions() - @atomEnvironment.saveState({isUnloading: true}) if confirmed @atomEnvironment.unloadEditorWindow() else From 8caa9d0a9571d48d812c5290abe97b56d154f750 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 7 Mar 2016 17:21:09 +0100 Subject: [PATCH 177/198] :art: Better wording on specs --- spec/atom-environment-spec.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 8e7af40d5..7488c8e05 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -179,19 +179,21 @@ describe "AtomEnvironment", -> atom.loadState().then (state) -> expect(state).toEqual({stuff: 'cool'}) - it "saves state on keydown and mousedown events when the editor window hasn't been unloaded", -> + it "saves state on keydown, mousedown, and when the editor window unloads", -> spyOn(atom, 'saveState') keydown = new KeyboardEvent('keydown') atom.document.dispatchEvent(keydown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) atom.saveState.reset() mousedown = new MouseEvent('mousedown') atom.document.dispatchEvent(mousedown) advanceClock atom.saveStateDebounceInterval expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false}) + expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true}) atom.saveState.reset() atom.unloadEditorWindow() From f6d419c2f4563dae446592eb928d4540bb841af7 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 7 Mar 2016 10:01:28 -0800 Subject: [PATCH 178/198] Merge pull request #11057 from atom/mkt-improve-pane-add-item-options Move Pane::addItem 'pending' option to options object (cherry picked from commit 53a9c22554177c787751e20c7e7b38bd4eb8e69b) --- package.json | 2 +- spec/pane-spec.coffee | 57 +++++++++++++++++++++++++++++++++++-------- src/pane.coffee | 41 ++++++++++++++++++++----------- src/workspace.coffee | 2 +- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index ff30c0d07..5cd1367fa 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.0", "styleguide": "0.45.2", "symbols-view": "0.111.1", - "tabs": "0.91.1", + "tabs": "0.91.3", "timecop": "0.33.1", "tree-view": "0.202.0", "update-package-dependencies": "0.10.0", diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 873749a19..d0b191f38 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -1,5 +1,6 @@ {extend} = require 'underscore-plus' {Emitter} = require 'event-kit' +Grim = require 'grim' Pane = require '../src/pane' PaneAxis = require '../src/pane-axis' PaneContainer = require '../src/pane-container' @@ -92,7 +93,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [new Item("A"), new Item("B")])) [item1, item2] = pane.getItems() item3 = new Item("C") - pane.addItem(item3, 1) + pane.addItem(item3, index: 1) expect(pane.getItems()).toEqual [item1, item3, item2] it "adds the item after the active item if no index is provided", -> @@ -115,7 +116,7 @@ describe "Pane", -> pane.onDidAddItem (event) -> events.push(event) item = new Item("C") - pane.addItem(item, 1) + pane.addItem(item, index: 1) expect(events).toEqual [{item, index: 1, moved: false}] it "throws an exception if the item is already present on a pane", -> @@ -137,9 +138,9 @@ describe "Pane", -> itemA = new Item("A") itemB = new Item("B") itemC = new Item("C") - pane.addItem(itemA, undefined, false, false) - pane.addItem(itemB, undefined, false, true) - pane.addItem(itemC, undefined, false, false) + pane.addItem(itemA, pending: false) + pane.addItem(itemB, pending: true) + pane.addItem(itemC, pending: false) expect(itemB.isDestroyed()).toBe true it "adds the new item before destroying any existing pending item", -> @@ -148,7 +149,7 @@ describe "Pane", -> pane = new Pane(paneParams(items: [])) itemA = new Item("A") itemB = new Item("B") - pane.addItem(itemA, undefined, false, true) + pane.addItem(itemA, pending: true) pane.onDidAddItem ({item}) -> eventOrder.push("add") if item is itemB @@ -164,6 +165,25 @@ describe "Pane", -> runs -> expect(eventOrder).toEqual ["add", "remove"] + describe "when using the old API of ::addItem(item, index)", -> + beforeEach -> + spyOn Grim, "deprecate" + + it "supports the older public API", -> + pane = new Pane(paneParams(items: [])) + itemA = new Item("A") + itemB = new Item("B") + itemC = new Item("C") + pane.addItem(itemA, 0) + pane.addItem(itemB, 0) + pane.addItem(itemC, 0) + expect(pane.getItems()).toEqual [itemC, itemB, itemA] + + it "shows a deprecation warning", -> + pane = new Pane(paneParams(items: [])) + pane.addItem(new Item(), 2) + expect(Grim.deprecate).toHaveBeenCalledWith "Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})" + describe "::activateItem(item)", -> pane = null @@ -196,15 +216,15 @@ describe "Pane", -> itemD = new Item("D") it "replaces the active item if it is pending", -> - pane.activateItem(itemC, true) + pane.activateItem(itemC, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B'] - pane.activateItem(itemD, true) + pane.activateItem(itemD, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B'] it "adds the item after the active item if it is not pending", -> - pane.activateItem(itemC, true) + pane.activateItem(itemC, pending: true) pane.activateItemAtIndex(2) - pane.activateItem(itemD, true) + pane.activateItem(itemD, pending: true) expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D'] describe "::setPendingItem", -> @@ -706,6 +726,23 @@ describe "Pane", -> expect(pane2.isDestroyed()).toBe true expect(item4.isDestroyed()).toBe false + describe "when the item being moved is pending", -> + it "is made permanent in the new pane", -> + item6 = new Item("F") + pane1.addItem(item6, pending: true) + expect(pane1.getPendingItem()).toEqual item6 + pane1.moveItemToPane(item6, pane2, 0) + expect(pane2.getPendingItem()).not.toEqual item6 + + describe "when the target pane has a pending item", -> + it "does not destroy the pending item", -> + item6 = new Item("F") + pane1.addItem(item6, pending: true) + expect(pane1.getPendingItem()).toEqual item6 + pane2.moveItemToPane(item5, pane1, 0) + expect(pane1.getPendingItem()).toEqual item6 + + describe "split methods", -> [pane1, item1, container] = [] diff --git a/src/pane.coffee b/src/pane.coffee index 70f8b4bab..3ff62993c 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,3 +1,4 @@ +Grim = require 'grim' {find, compact, extend, last} = require 'underscore-plus' {CompositeDisposable, Emitter} = require 'event-kit' Model = require './model' @@ -394,30 +395,42 @@ class Pane extends Model # Public: Make the given item *active*, causing it to be displayed by # the pane's view. # - # * `pending` (optional) {Boolean} indicating that the item should be added - # in a pending state if it does not yet exist in the pane. Existing pending - # items in a pane are replaced with new pending items when they are opened. - activateItem: (item, pending=false) -> + # * `options` (optional) {Object} + # * `pending` (optional) {Boolean} indicating that the item should be added + # in a pending state if it does not yet exist in the pane. Existing pending + # items in a pane are replaced with new pending items when they are opened. + activateItem: (item, options={}) -> if item? if @getPendingItem() is @activeItem index = @getActiveItemIndex() else index = @getActiveItemIndex() + 1 - @addItem(item, index, false, pending) + @addItem(item, extend({}, options, {index: index})) @setActiveItem(item) # Public: Add the given item to the pane. # # * `item` The item to add. It can be a model with an associated view or a # view. - # * `index` (optional) {Number} indicating the index at which to add the item. - # If omitted, the item is added after the current active item. - # * `pending` (optional) {Boolean} indicating that the item should be - # added in a pending state. Existing pending items in a pane are replaced with - # new pending items when they are opened. + # * `options` (optional) {Object} + # * `index` (optional) {Number} indicating the index at which to add the item. + # If omitted, the item is added after the current active item. + # * `pending` (optional) {Boolean} indicating that the item should be + # added in a pending state. Existing pending items in a pane are replaced with + # new pending items when they are opened. # # Returns the added item. - addItem: (item, index=@getActiveItemIndex() + 1, moved=false, pending=false) -> + addItem: (item, options={}) -> + # Backward compat with old API: + # addItem(item, index=@getActiveItemIndex() + 1) + if typeof options is "number" + Grim.deprecate("Pane::addItem(item, #{options}) is deprecated in favor of Pane::addItem(item, {index: #{options}})") + options = index: options + + index = options.index ? @getActiveItemIndex() + 1 + moved = options.moved ? false + pending = options.pending ? false + throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object' throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?() @@ -437,7 +450,7 @@ class Pane extends Model @setPendingItem(item) if pending @emitter.emit 'did-add-item', {item, index, moved} - @destroyItem(lastPendingItem) if lastPendingItem? + @destroyItem(lastPendingItem) if lastPendingItem? and not moved @setActiveItem(item) unless @getActiveItem()? item @@ -467,7 +480,7 @@ class Pane extends Model # Returns an {Array} of added items. addItems: (items, index=@getActiveItemIndex() + 1) -> items = items.filter (item) => not (item in @items) - @addItem(item, index + i, false) for item, i in items + @addItem(item, {index: index + i}) for item, i in items items removeItem: (item, moved) -> @@ -516,7 +529,7 @@ class Pane extends Model # given pane. moveItemToPane: (item, pane, index) -> @removeItem(item, true) - pane.addItem(item, index, true) + pane.addItem(item, {index: index, moved: true}) # Public: Destroy the active item and activate the next item. destroyActiveItem: -> diff --git a/src/workspace.coffee b/src/workspace.coffee index 873d90370..9b1bf14fc 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -509,7 +509,7 @@ class Workspace extends Model return item if pane.isDestroyed() @itemOpened(item) - pane.activateItem(item, options.pending) if activateItem + pane.activateItem(item, {pending: options.pending}) if activateItem pane.activate() if activatePane initialLine = initialColumn = 0 From 56d82ccac28467c6c7c5f1114af09ac420bccb24 Mon Sep 17 00:00:00 2001 From: joshaber Date: Mon, 7 Mar 2016 14:44:51 -0500 Subject: [PATCH 179/198] :arrow_up: nodegit@0.11.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cd1367fa..83864702d 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.6", + "nodegit": "0.11.7", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From 56b23a4a10b01a97497b08c9909f98dbf15867f9 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 7 Mar 2016 14:01:05 -0800 Subject: [PATCH 180/198] Make cli atom --wait work on Cygwin --- atom.sh | 2 -- resources/win/atom.sh | 19 ++++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/atom.sh b/atom.sh index ef8dbcdc4..b68716bf4 100755 --- a/atom.sh +++ b/atom.sh @@ -4,8 +4,6 @@ if [ "$(uname)" == 'Darwin' ]; then OS='Mac' elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then OS='Linux' -elif [ "$(expr substr $(uname -s) 1 10)" == 'MINGW32_NT' ]; then - OS='Cygwin' else echo "Your platform ($(uname -a)) is not supported." exit 1 diff --git a/resources/win/atom.sh b/resources/win/atom.sh index 0eaf193c0..7f1b1e093 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -6,6 +6,7 @@ while getopts ":fhtvw-:" opt; do case "${OPTARG}" in wait) WAIT=1 + EXPECT_OUTPUT=1 ;; help|version) REDIRECT_STDERR=1 @@ -18,6 +19,7 @@ while getopts ":fhtvw-:" opt; do ;; w) WAIT=1 + EXPECT_OUTPUT=1 ;; h|v) REDIRECT_STDERR=1 @@ -31,19 +33,14 @@ done directory=$(dirname "$0") -WINPS=`ps | grep -i $$` -PID=`echo $WINPS | cut -d' ' -f 4` - if [ $EXPECT_OUTPUT ]; then export ELECTRON_ENABLE_LOGGING=1 - "$directory/../../atom.exe" --executed-from="$(pwd)" --pid=$PID "$@" + if [ $WAIT == 'YES' ]; then + powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid "$@" ; +wait-event + else + "$directory/../../atom.exe" "$@" + fi else "$directory/../app/apm/bin/node.exe" "$directory/atom.js" "$@" fi - -# If the wait flag is set, don't exit this process until Atom tells it to. -if [ $WAIT ]; then - while true; do - sleep 1 - done -fi From b53c5a10d00db421abf84116d824addbd9a7c9ee Mon Sep 17 00:00:00 2001 From: Rowan Bottema Date: Tue, 8 Mar 2016 10:18:43 +0100 Subject: [PATCH 181/198] Add zero to hexadecimal numbers below F (16) --- spec/config-spec.coffee | 10 ++++++++++ src/color.coffee | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index c7485ea65..acd9b112b 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -1621,6 +1621,16 @@ describe "Config", -> expect(color.toHexString()).toBe '#ff0000' expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)' + color.red = 11 + color.green = 11 + color.blue = 124 + color.alpha = 1 + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe '#0b0b7c' + expect(color.toRGBAString()).toBe 'rgba(11, 11, 124, 1)' + it 'coerces various types to a color object', -> atom.config.set('foo.bar.aColor', 'red') expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} diff --git a/src/color.coffee b/src/color.coffee index fc751ce42..b413b9e2c 100644 --- a/src/color.coffee +++ b/src/color.coffee @@ -85,5 +85,5 @@ parseAlpha = (alpha) -> numberToHexString = (number) -> hex = number.toString(16) - hex = "0#{hex}" if number < 10 + hex = "0#{hex}" if number < 16 hex From 2f401c7116ee7103adbdc39c0968c1328e9e81e5 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:14:26 -0500 Subject: [PATCH 182/198] We should emit changes when anything changes. --- spec/git-repository-async-spec.js | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index e7bffad3a..9144f8611 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -422,6 +422,44 @@ describe('GitRepositoryAsync', () => { expect(repo.isStatusModified(status)).toBe(true) expect(repo.isStatusNew(status)).toBe(false) }) + + it('emits did-change-statuses if the status changes', async () => { + const someNewPath = path.join(workingDirectory, 'MyNewJSFramework.md') + fs.writeFileSync(someNewPath, '') + + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) + + it('emits did-change-statuses if the branch changes', async () => { + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + repo._refreshBranch = jasmine.createSpy('_refreshBranch').andCallFake(() => { + return Promise.resolve(true) + }) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) + + it('emits did-change-statuses if the ahead/behind changes', async () => { + const statusHandler = jasmine.createSpy('statusHandler') + repo.onDidChangeStatuses(statusHandler) + + repo._refreshAheadBehindCount = jasmine.createSpy('_refreshAheadBehindCount').andCallFake(() => { + return Promise.resolve(true) + }) + + await repo.refreshStatus() + + waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) + }) }) describe('.isProjectAtRoot()', () => { From f2be54bf299355142b4a9d79a3ea8fda19964986 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:14:42 -0500 Subject: [PATCH 183/198] But actually do it tho. --- src/git-repository-async.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 91b78e0c5..642ed5a98 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -783,12 +783,17 @@ export default class GitRepositoryAsync { // Get the current branch and update this.branch. // - // Returns a {Promise} which resolves to the {String} branch name. + // Returns a {Promise} which resolves to a {boolean} indicating whether the + // branch name changed. _refreshBranch () { return this.getRepo() .then(repo => repo.getCurrentBranch()) .then(ref => ref.name()) - .then(branchName => this.branch = branchName) + .then(branchName => { + const changed = branchName !== this.branch + this.branch = branchName + return changed + }) } // Refresh the cached ahead/behind count with the given branch. @@ -796,10 +801,15 @@ export default class GitRepositoryAsync { // * `branchName` The {String} name of the branch whose ahead/behind should be // used for the refresh. // - // Returns a {Promise} which will resolve to {null}. + // Returns a {Promise} which will resolve to a {boolean} indicating whether + // the ahead/behind count changed. _refreshAheadBehindCount (branchName) { return this.getAheadBehindCount(branchName) - .then(counts => this.upstream = counts) + .then(counts => { + const changed = !_.isEqual(counts, this.upstream) + this.upstream = counts + return changed + }) } // Get the status for this repository. @@ -905,15 +915,15 @@ export default class GitRepositoryAsync { // Refresh the cached status. // - // Returns a {Promise} which will resolve to {null}. + // Returns a {Promise} which will resolve to a {boolean} indicating whether + // any statuses changed. _refreshStatus () { return Promise.all([this._getRepositoryStatus(), this._getSubmoduleStatuses()]) .then(([repositoryStatus, submoduleStatus]) => { const statusesByPath = _.extend({}, repositoryStatus, submoduleStatus) - if (!_.isEqual(this.pathStatusCache, statusesByPath) && this.emitter != null) { - this.emitter.emit('did-change-statuses') - } + const changed = !_.isEqual(this.pathStatusCache, statusesByPath) this.pathStatusCache = statusesByPath + return changed }) } @@ -927,7 +937,13 @@ export default class GitRepositoryAsync { this._refreshingPromise = this._refreshingPromise.then(_ => { return Promise.all([status, branch, aheadBehind]) - .then(_ => null) + .then(([statusChanged, branchChanged, aheadBehindChanged]) => { + if (this.emitter && (statusChanged || branchChanged || aheadBehindChanged)) { + this.emitter.emit('did-change-statuses') + } + + return null + }) // Because all these refresh steps happen asynchronously, it's entirely // possible the repository was destroyed while we were working. In which // case we should just swallow the error. From cb44450e923ec6040c4b7151d0106fd2dbd52f41 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:40:29 -0500 Subject: [PATCH 184/198] Correct the spec name. --- spec/git-repository-async-spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 9144f8611..900d81bfb 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -714,7 +714,7 @@ describe('GitRepositoryAsync', () => { repo = GitRepositoryAsync.open(workingDirectory) }) - it('returns 0, 0 for a branch with no upstream', async () => { + it('returns 1, 0 for a branch which is ahead by 1', async () => { await repo.refreshStatus() const {ahead, behind} = await repo.getCachedUpstreamAheadBehindCount('You-Dont-Need-jQuery') From fc62398d62b950c6d84f5a6836a5e781a868d9f7 Mon Sep 17 00:00:00 2001 From: joshaber Date: Tue, 8 Mar 2016 12:40:38 -0500 Subject: [PATCH 185/198] Use the new branch name. --- src/git-repository-async.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git-repository-async.js b/src/git-repository-async.js index 642ed5a98..f80f46a13 100644 --- a/src/git-repository-async.js +++ b/src/git-repository-async.js @@ -933,7 +933,7 @@ export default class GitRepositoryAsync { refreshStatus () { const status = this._refreshStatus() const branch = this._refreshBranch() - const aheadBehind = branch.then(branchName => this._refreshAheadBehindCount(branchName)) + const aheadBehind = branch.then(() => this._refreshAheadBehindCount(this.branch)) this._refreshingPromise = this._refreshingPromise.then(_ => { return Promise.all([status, branch, aheadBehind]) From 42c6d5c2bcb3c6b1d91a03bf92dd015b0b2cd4f6 Mon Sep 17 00:00:00 2001 From: Daniel Hengeveld Date: Tue, 8 Mar 2016 18:47:42 +0100 Subject: [PATCH 186/198] :arrow_up:symbols-view@0.112.0 - adds es7 async functions to js --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 83864702d..0353c9957 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "spell-check": "0.67.0", "status-bar": "1.1.0", "styleguide": "0.45.2", - "symbols-view": "0.111.1", + "symbols-view": "0.112.0", "tabs": "0.91.3", "timecop": "0.33.1", "tree-view": "0.202.0", From 52d6fbde1b9a4f869c8063972357ea19abce060f Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 8 Mar 2016 12:12:52 -0800 Subject: [PATCH 187/198] More reliable to just call atom.cmd for Cygwin --- resources/win/atom.sh | 46 +------------------------------------------ 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/resources/win/atom.sh b/resources/win/atom.sh index 7f1b1e093..7380bf122 100644 --- a/resources/win/atom.sh +++ b/resources/win/atom.sh @@ -1,46 +1,2 @@ #!/bin/sh - -while getopts ":fhtvw-:" opt; do - case "$opt" in - -) - case "${OPTARG}" in - wait) - WAIT=1 - EXPECT_OUTPUT=1 - ;; - help|version) - REDIRECT_STDERR=1 - EXPECT_OUTPUT=1 - ;; - foreground|test) - EXPECT_OUTPUT=1 - ;; - esac - ;; - w) - WAIT=1 - EXPECT_OUTPUT=1 - ;; - h|v) - REDIRECT_STDERR=1 - EXPECT_OUTPUT=1 - ;; - f|t) - EXPECT_OUTPUT=1 - ;; - esac -done - -directory=$(dirname "$0") - -if [ $EXPECT_OUTPUT ]; then - export ELECTRON_ENABLE_LOGGING=1 - if [ $WAIT == 'YES' ]; then - powershell -noexit "%~dp0\..\..\atom.exe" --pid=$pid "$@" ; -wait-event - else - "$directory/../../atom.exe" "$@" - fi -else - "$directory/../app/apm/bin/node.exe" "$directory/atom.js" "$@" -fi +$(dirname "$0")/atom.cmd "$@" From bf5dea0a27953beafa40c12943b1fff6d7c2b09f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 8 Mar 2016 19:25:52 -0700 Subject: [PATCH 188/198] :arrow_up: fuzzy-finder --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0353c9957..59264c3a5 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.197.4", - "fuzzy-finder": "1.0.2", + "fuzzy-finder": "1.0.3", "git-diff": "1.0.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.1", From 152e370a15e65a09039ddf82f1528563d9eb94c4 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Wed, 9 Mar 2016 02:16:10 -0800 Subject: [PATCH 189/198] Default the options parameter to an empty object --- src/project.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project.coffee b/src/project.coffee index 5f3420b08..93a3ed496 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -71,7 +71,7 @@ class Project extends Model @subscribeToBuffer(buffer) for buffer in @buffers @setPaths(state.paths) - serialize: (options) -> + serialize: (options={}) -> deserializer: 'Project' paths: @getPaths() buffers: _.compact(@buffers.map (buffer) -> buffer.serialize({markerLayers: options.isUnloading is true}) if buffer.isRetained()) From 129139f03a2846506d87dfa393d9d0b525e75757 Mon Sep 17 00:00:00 2001 From: joshaber Date: Wed, 9 Mar 2016 12:43:28 -0500 Subject: [PATCH 190/198] :arrow_up: nodegit@0.11.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59264c3a5..a1fa3e820 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "less-cache": "0.23", "line-top-index": "0.2.0", "marked": "^0.3.4", - "nodegit": "0.11.7", + "nodegit": "0.11.9", "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "^5", From a04c552fb710c3b147a55d824f260beac6cb3e34 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Wed, 9 Mar 2016 19:22:23 +0100 Subject: [PATCH 191/198] :arrow_up: language-text --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 59264c3a5..c96d52b72 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "language-shellscript": "0.21.0", "language-source": "0.9.0", "language-sql": "0.20.0", - "language-text": "0.7.0", + "language-text": "0.7.1", "language-todo": "0.27.0", "language-toml": "0.18.0", "language-xml": "0.34.4", From ac35fec0cd882394d0ba22a98d1d7280e96a1f5d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 9 Mar 2016 17:26:47 -0800 Subject: [PATCH 192/198] :arrow_up: tree-view@0.203.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8bae63604..6b449d0de 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "symbols-view": "0.112.0", "tabs": "0.91.3", "timecop": "0.33.1", - "tree-view": "0.202.0", + "tree-view": "0.203.0", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From 4b017759c9b053595e056d9ca409efee8dc81ff9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 10 Mar 2016 09:40:26 +0100 Subject: [PATCH 193/198] Ensure project.serialize is called with atom.saveState options This is because we have made the `project.serialize(options)` parameter optional for backwards compatibility (i.e. #11111), and we want to make sure we don't make the mistake of not passing it internally. --- spec/atom-environment-spec.coffee | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/atom-environment-spec.coffee b/spec/atom-environment-spec.coffee index 7488c8e05..3283b63d6 100644 --- a/spec/atom-environment-spec.coffee +++ b/spec/atom-environment-spec.coffee @@ -203,6 +203,14 @@ describe "AtomEnvironment", -> expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true}) expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false}) + it "serializes the project state with all the options supplied in saveState", -> + spyOn(atom.project, 'serialize').andReturn({foo: 42}) + + waitsForPromise -> atom.saveState({anyOption: 'any option'}) + runs -> + expect(atom.project.serialize.calls.length).toBe(1) + expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({anyOption: 'any option'}) + describe "openInitialEmptyEditorIfNecessary", -> describe "when there are no paths set", -> beforeEach -> From 0d7b182c2dba7daedc605ee4a65ba2c892842c5a Mon Sep 17 00:00:00 2001 From: joshaber Date: Thu, 10 Mar 2016 10:31:24 -0500 Subject: [PATCH 194/198] :arrow_up: status-bar@1.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6b449d0de..353e470be 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "settings-view": "0.232.4", "snippets": "1.0.1", "spell-check": "0.67.0", - "status-bar": "1.1.0", + "status-bar": "1.1.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", "tabs": "0.91.3", From 501eadccca0c8aaad9f7b027c596a87e2bcd873a Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 10 Mar 2016 11:00:48 -0800 Subject: [PATCH 195/198] :arrow_up: notifications@0.62.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 353e470be..d2cb82195 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "link": "0.31.0", "markdown-preview": "0.157.3", "metrics": "0.53.1", - "notifications": "0.62.3", + "notifications": "0.62.4", "open-on-github": "1.0.0", "package-generator": "0.41.1", "settings-view": "0.232.4", From 4e2db07dd96c13c966f84a86c01ec3ca9d7048cd Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 11 Mar 2016 10:13:31 +0900 Subject: [PATCH 196/198] :arrow_up: markdown-preview@v0.158.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2cb82195..0194a5ad4 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "keybinding-resolver": "0.35.0", "line-ending-selector": "0.3.1", "link": "0.31.0", - "markdown-preview": "0.157.3", + "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.62.4", "open-on-github": "1.0.0", From bd4ef0a44381a6445d17063ff944bc91fd54a1ce Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Thu, 10 Mar 2016 20:17:19 -0800 Subject: [PATCH 197/198] :arrow_up: tabs@0.92.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2cb82195..0b823926b 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "status-bar": "1.1.1", "styleguide": "0.45.2", "symbols-view": "0.112.0", - "tabs": "0.91.3", + "tabs": "0.92.0", "timecop": "0.33.1", "tree-view": "0.203.0", "update-package-dependencies": "0.10.0", From 383174d3803ef5e5ce658be1a94529516836b0b6 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 10 Mar 2016 21:27:10 -0800 Subject: [PATCH 198/198] Load apm path from config Signed-off-by: Katrina Uychaco --- spec/package-manager-spec.coffee | 14 ++++++++++++++ src/package-manager.coffee | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 3b54691b2..6a1610a8a 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -17,6 +17,20 @@ describe "PackageManager", -> beforeEach -> workspaceElement = atom.views.getView(atom.workspace) + describe "::getApmPath()", -> + it "returns the path to the apm command", -> + apmPath = path.join(process.resourcesPath, "app", "apm", "bin", "apm") + if process.platform is 'win32' + apmPath += ".cmd" + expect(atom.packages.getApmPath()).toBe apmPath + + describe "when the core.apmPath setting is set", -> + beforeEach -> + atom.config.set("core.apmPath", "/path/to/apm") + + it "returns the value of the core.apmPath config setting", -> + expect(atom.packages.getApmPath()).toBe "/path/to/apm" + describe "::loadPackage(name)", -> beforeEach -> atom.config.set("core.disabledPackages", []) diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 94b55a793..0e76a762f 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -128,8 +128,12 @@ class PackageManager # Public: Get the path to the apm command. # + # Uses the value of the `core.apmPath` config setting if it exists. + # # Return a {String} file path to apm. getApmPath: -> + configPath = atom.config.get('core.apmPath') + return configPath if configPath return @apmPath if @apmPath? commandName = 'apm'