From f96a0d922ed95949eb67b530d95ce808409086f5 Mon Sep 17 00:00:00 2001 From: Ford Hurley Date: Wed, 27 Dec 2017 13:39:50 -0500 Subject: [PATCH 1/7] Ensure that new editors get unique ids This restores the behavior from when TextEditor was written in coffeescript, and extended the Model class. --- src/text-editor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/text-editor.js b/src/text-editor.js index 05e510b75..6f9993eed 100644 --- a/src/text-editor.js +++ b/src/text-editor.js @@ -119,6 +119,10 @@ class TextEditor { } this.id = params.id != null ? params.id : nextId++ + if (this.id >= nextId) { + // Ensure that new editors get unique ids: + nextId = this.id + 1; + } this.initialScrollTopRow = params.initialScrollTopRow this.initialScrollLeftColumn = params.initialScrollLeftColumn this.decorationManager = params.decorationManager From b5189e4e4ab90cc72ba1767d2462496a2b57fa21 Mon Sep 17 00:00:00 2001 From: Ford Hurley Date: Wed, 27 Dec 2017 15:16:13 -0500 Subject: [PATCH 2/7] Delint --- src/text-editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/text-editor.js b/src/text-editor.js index 6f9993eed..18c767f81 100644 --- a/src/text-editor.js +++ b/src/text-editor.js @@ -121,7 +121,7 @@ class TextEditor { this.id = params.id != null ? params.id : nextId++ if (this.id >= nextId) { // Ensure that new editors get unique ids: - nextId = this.id + 1; + nextId = this.id + 1 } this.initialScrollTopRow = params.initialScrollTopRow this.initialScrollLeftColumn = params.initialScrollLeftColumn From 3ad3852dd6a2fbdc5d64f21d4838fdd81ff1dbc4 Mon Sep 17 00:00:00 2001 From: Ford Hurley Date: Wed, 27 Dec 2017 15:16:22 -0500 Subject: [PATCH 3/7] Add a test for generated TextEditor ids --- spec/text-editor-spec.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index 89af72137..afd0d9068 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -20,6 +20,16 @@ describe('TextEditor', () => { await atom.packages.activatePackage('language-javascript') }) + it('generates unique ids for each editor', () => { + // Deserialized editors are initialized with an id: + new TextEditor({id: 0}) + new TextEditor({id: 1}) + new TextEditor({id: 2}) + // Initializing an editor without an id causes a new id to be generated: + const generatedId = new TextEditor().id + expect(generatedId).toBe(3) + }) + describe('when the editor is deserialized', () => { it('restores selections and folds based on markers in the buffer', async () => { editor.setSelectedBufferRange([[1, 2], [3, 4]]) From 798bbe3c32938aaf6e5677af05622df8459f5cec Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 27 Dec 2017 13:35:01 -0800 Subject: [PATCH 4/7] Revert "Independent Atom instances (per $ATOM_HOME)" --- src/main-process/atom-application.coffee | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 8e889ecca..f6802705e 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -8,7 +8,6 @@ FileRecoveryService = require './file-recovery-service' ipcHelpers = require '../ipc-helpers' {BrowserWindow, Menu, app, dialog, ipcMain, shell, screen} = require 'electron' {CompositeDisposable, Disposable} = require 'event-kit' -crypto = require 'crypto' fs = require 'fs-plus' path = require 'path' os = require 'os' @@ -34,16 +33,11 @@ class AtomApplication # Public: The entry point into the Atom application. @open: (options) -> unless options.socketPath? - username = if process.platform is 'win32' then process.env.USERNAME else process.env.USER - # Lowercasing the ATOM_HOME to make sure that we don't get multiple sockets - # on case-insensitive filesystems due to arbitrary case differences in paths. - atomHomeUnique = path.resolve(process.env.ATOM_HOME).toLowerCase() - hash = crypto.createHash('sha1').update(username).update('|').update(atomHomeUnique) - atomInstanceDigest = hash.digest('hex').substring(0, 32) if process.platform is 'win32' - options.socketPath = "\\\\.\\pipe\\atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}-sock" + userNameSafe = new Buffer(process.env.USERNAME).toString('base64') + options.socketPath = "\\\\.\\pipe\\atom-#{options.version}-#{userNameSafe}-#{process.arch}-sock" else - options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}.sock") + options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.env.USER}.sock") # FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely # take a few seconds to trigger 'error' event, it could be a bug of node From 065f4c48ec66654a604af5408fb5efb6ddb461fa Mon Sep 17 00:00:00 2001 From: Ford Hurley Date: Wed, 27 Dec 2017 16:37:12 -0500 Subject: [PATCH 5/7] Avoid dependency on shared state The test was passing only when run in isolation. --- spec/text-editor-spec.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index afd0d9068..ab84d88c8 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -20,14 +20,15 @@ describe('TextEditor', () => { await atom.packages.activatePackage('language-javascript') }) - it('generates unique ids for each editor', () => { - // Deserialized editors are initialized with an id: - new TextEditor({id: 0}) - new TextEditor({id: 1}) - new TextEditor({id: 2}) - // Initializing an editor without an id causes a new id to be generated: - const generatedId = new TextEditor().id - expect(generatedId).toBe(3) + it('generates unique ids for each editor', async () => { + // Deserialized editors are initialized with the serialized id. We can + // initialize an editor with what we expect to be the next id: + const deserialized = new TextEditor({id: editor.id+1}) + expect(deserialized.id).toEqual(editor.id+1) + + // The id generator should skip the id used up by the deserialized one: + const fresh = new TextEditor() + expect(fresh.id).toNotEqual(deserialized.id) }) describe('when the editor is deserialized', () => { From 2b3e22a39d689592d16cd0c466508281c51e28bf Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Tue, 18 Jul 2017 10:40:24 +1200 Subject: [PATCH 6/7] Allow independent Atom instances By having an $ATOM_HOME-dependent part in the socket name, Atom instances that have different homes will run in independent processes. Fixes the current behaviour where starting Atom with a new $ATOM_HOME "opens" an Atom window with settings and packages from the original $ATOM_HOME. Useful for IDEs. --- src/main-process/atom-application.coffee | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index f6802705e..8e889ecca 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -8,6 +8,7 @@ FileRecoveryService = require './file-recovery-service' ipcHelpers = require '../ipc-helpers' {BrowserWindow, Menu, app, dialog, ipcMain, shell, screen} = require 'electron' {CompositeDisposable, Disposable} = require 'event-kit' +crypto = require 'crypto' fs = require 'fs-plus' path = require 'path' os = require 'os' @@ -33,11 +34,16 @@ class AtomApplication # Public: The entry point into the Atom application. @open: (options) -> unless options.socketPath? + username = if process.platform is 'win32' then process.env.USERNAME else process.env.USER + # Lowercasing the ATOM_HOME to make sure that we don't get multiple sockets + # on case-insensitive filesystems due to arbitrary case differences in paths. + atomHomeUnique = path.resolve(process.env.ATOM_HOME).toLowerCase() + hash = crypto.createHash('sha1').update(username).update('|').update(atomHomeUnique) + atomInstanceDigest = hash.digest('hex').substring(0, 32) if process.platform is 'win32' - userNameSafe = new Buffer(process.env.USERNAME).toString('base64') - options.socketPath = "\\\\.\\pipe\\atom-#{options.version}-#{userNameSafe}-#{process.arch}-sock" + options.socketPath = "\\\\.\\pipe\\atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}-sock" else - options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.env.USER}.sock") + options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}.sock") # FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely # take a few seconds to trigger 'error' event, it could be a bug of node From 1964b0094b611904ab177ef9963501bb94b08ce6 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Thu, 28 Dec 2017 13:06:52 +1300 Subject: [PATCH 7/7] Make socketPath shorter To work around the limited socket file length on macOS/BSD. --- src/main-process/atom-application.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 8e889ecca..e0d2d691f 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -38,12 +38,15 @@ class AtomApplication # Lowercasing the ATOM_HOME to make sure that we don't get multiple sockets # on case-insensitive filesystems due to arbitrary case differences in paths. atomHomeUnique = path.resolve(process.env.ATOM_HOME).toLowerCase() - hash = crypto.createHash('sha1').update(username).update('|').update(atomHomeUnique) - atomInstanceDigest = hash.digest('hex').substring(0, 32) + hash = crypto.createHash('sha1').update(options.version).update('|').update(process.arch).update('|').update(username).update('|').update(atomHomeUnique) + # We only keep the first 12 characters of the hash as not to have excessively long + # socket file. Note that macOS/BSD limit the length of socket file paths (see #15081). + # The replace calls convert the digest into "URL and Filename Safe" encoding (see RFC 4648). + atomInstanceDigest = hash.digest('base64').substring(0, 12).replace(/\+/g, '-').replace(/\//g, '_') if process.platform is 'win32' - options.socketPath = "\\\\.\\pipe\\atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}-sock" + options.socketPath = "\\\\.\\pipe\\atom-#{atomInstanceDigest}-sock" else - options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.arch}-#{atomInstanceDigest}.sock") + options.socketPath = path.join(os.tmpdir(), "atom-#{atomInstanceDigest}.sock") # FIXME: Sometimes when socketPath doesn't exist, net.connect would strangely # take a few seconds to trigger 'error' event, it could be a bug of node