diff --git a/exports/atom.js b/exports/atom.js index d7ca55909..359f0174e 100644 --- a/exports/atom.js +++ b/exports/atom.js @@ -1,13 +1,12 @@ -/** @babel */ - -import TextBuffer, {Point, Range} from 'text-buffer' -import {File, Directory} from 'pathwatcher' -import {Emitter, Disposable, CompositeDisposable} from 'event-kit' -import BufferedNodeProcess from '../src/buffered-node-process' -import BufferedProcess from '../src/buffered-process' -import GitRepository from '../src/git-repository' -import Notification from '../src/notification' -import {watchPath} from '../src/path-watcher' +const TextBuffer = require('text-buffer') +const {Point, Range} = TextBuffer +const {File, Directory} = require('pathwatcher') +const {Emitter, Disposable, CompositeDisposable} = require('event-kit') +const BufferedNodeProcess = require('../src/buffered-node-process') +const BufferedProcess = require('../src/buffered-process') +const GitRepository = require('../src/git-repository') +const Notification = require('../src/notification') +const {watchPath} = require('../src/path-watcher') const atomExport = { BufferedNodeProcess, @@ -42,4 +41,4 @@ if (process.type === 'renderer') { atomExport.TextEditor = require('../src/text-editor') } -export default atomExport +module.exports = atomExport diff --git a/package.json b/package.json index 974a9ab95..8b053850b 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "autosave": "0.24.6", "background-tips": "0.27.1", "bookmarks": "0.45.1", - "bracket-matcher": "0.89.0", + "bracket-matcher": "0.89.1", "command-palette": "0.43.0", "dalek": "0.2.1", "deprecation-cop": "0.56.9", diff --git a/script/lib/transpile-babel-paths.js b/script/lib/transpile-babel-paths.js index b7906b9b6..befd2477b 100644 --- a/script/lib/transpile-babel-paths.js +++ b/script/lib/transpile-babel-paths.js @@ -16,9 +16,6 @@ module.exports = function () { function getPathsToTranspile () { let paths = [] - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'benchmarks', '**', '*.js'), {nodir: true})) - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'exports', '**', '*.js'), {nodir: true})) - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'src', '**', '*.js'), {nodir: true})) for (let packageName of Object.keys(CONFIG.appMetadata.packageDependencies)) { paths = paths.concat(glob.sync( path.join(CONFIG.intermediateAppPath, 'node_modules', packageName, '**', '*.js'), diff --git a/spec/atom-environment-spec.js b/spec/atom-environment-spec.js index 324e9eddf..5574e9663 100644 --- a/spec/atom-environment-spec.js +++ b/spec/atom-environment-spec.js @@ -4,7 +4,6 @@ const fs = require('fs') const path = require('path') const temp = require('temp').track() const AtomEnvironment = require('../src/atom-environment') -const StorageFolder = require('../src/storage-folder') describe('AtomEnvironment', () => { afterEach(() => { diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 90a512692..16aef8e27 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -49,7 +49,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':3'])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':3'])) await focusWindow(window) const cursorRow = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -66,7 +66,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':2:2'])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':2:2'])) await focusWindow(window) const cursorPosition = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -83,7 +83,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':: '])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':: '])) await focusWindow(window) const openedPath = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -99,11 +99,11 @@ describe('AtomApplication', function () { it('positions new windows at an offset distance from the previous window', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window1] = await atomApplication.launch(parseCommandLine([makeTempDir()])) await focusWindow(window1) window1.browserWindow.setBounds({width: 400, height: 400, x: 0, y: 0}) - const window2 = atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window2] = await atomApplication.launch(parseCommandLine([makeTempDir()])) await focusWindow(window2) assert.notEqual(window1, window2) @@ -122,7 +122,7 @@ describe('AtomApplication', function () { fs.writeFileSync(existingDirCFilePath, 'this is an existing file') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) await emitterEventPromise(window1, 'window:locations-opened') await focusWindow(window1) @@ -135,7 +135,7 @@ describe('AtomApplication', function () { // Reuses the window when opening *files*, even if they're in a different directory // Does not change the project paths when doing so. - const reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath])) assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -148,7 +148,7 @@ describe('AtomApplication', function () { assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath]) // Opens new windows when opening directories - const window2 = atomApplication.launch(parseCommandLine([dirCPath])) + const [window2] = await atomApplication.launch(parseCommandLine([dirCPath])) await emitterEventPromise(window2, 'window:locations-opened') assert.notEqual(window2, window1) await focusWindow(window2) @@ -163,7 +163,7 @@ describe('AtomApplication', function () { fs.writeFileSync(existingDirCFilePath, 'this is an existing file') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) await focusWindow(window1) let activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -175,7 +175,7 @@ describe('AtomApplication', function () { // When opening *files* with --add, reuses an existing window and adds // parent directory to the project - let reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) + let [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -189,7 +189,7 @@ describe('AtomApplication', function () { // When opening *directories* with add reuses an existing window and adds // the directory to the project - reusedWindow = atomApplication.launch(parseCommandLine([dirBPath, '-a'])) + reusedWindow = (await atomApplication.launch(parseCommandLine([dirBPath, '-a'])))[0] assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) @@ -202,7 +202,7 @@ describe('AtomApplication', function () { const atomApplication = buildAtomApplication() const nonExistentFilePath = path.join(tempDirPath, 'new-file') - const window1 = atomApplication.launch(parseCommandLine([nonExistentFilePath])) + const [window1] = await atomApplication.launch(parseCommandLine([nonExistentFilePath])) await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.observeTextEditors(textEditor => { textEditor.insertText('Hello World!') @@ -214,7 +214,7 @@ describe('AtomApplication', function () { await window1.closedPromise // Restore unsaved state when opening the directory itself - const window2 = atomApplication.launch(parseCommandLine([tempDirPath])) + const [window2] = await atomApplication.launch(parseCommandLine([tempDirPath])) await window2.loadedPromise const window2Text = await evalInWebContents(window2.browserWindow.webContents, sendBackToMainProcess => { const textEditor = atom.workspace.getActiveTextEditor() @@ -228,7 +228,7 @@ describe('AtomApplication', function () { await window2.closedPromise // Restore unsaved state when opening a path to a non-existent file in the directory - const window3 = atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'another-non-existent-file')])) + const [window3] = await atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'another-non-existent-file')])) await window3.loadedPromise const window3Texts = await evalInWebContents(window3.browserWindow.webContents, (sendBackToMainProcess, nonExistentFilePath) => { sendBackToMainProcess(atom.workspace.getTextEditors().map(editor => editor.getText())) @@ -243,7 +243,7 @@ describe('AtomApplication', function () { fs.mkdirSync(dirBSubdirPath) const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([dirAPath, dirBPath])) + const [window1] = await atomApplication.launch(parseCommandLine([dirAPath, dirBPath])) await focusWindow(window1) assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath]) @@ -252,17 +252,17 @@ describe('AtomApplication', function () { it('reuses windows with no project paths to open directories', async () => { const tempDirPath = makeTempDir() const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) - const reusedWindow = atomApplication.launch(parseCommandLine([tempDirPath])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine([tempDirPath])) assert.equal(reusedWindow, window1) await conditionPromise(async () => (await getTreeViewRootDirectories(reusedWindow)).length > 0) }) it('opens a new window with a single untitled buffer when launched with no path, even if windows already exist', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) const window1EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle()) @@ -287,7 +287,7 @@ describe('AtomApplication', function () { season.writeFileSync(configPath, config) const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) // wait a bit just to make sure we don't pass due to querying the render process before it loads @@ -302,7 +302,7 @@ describe('AtomApplication', function () { it('opens an empty text editor and loads its parent directory in the tree-view when launched with a new file path', async () => { const atomApplication = buildAtomApplication() const newFilePath = path.join(makeTempDir(), 'new-file') - const window = atomApplication.launch(parseCommandLine([newFilePath])) + const [window] = await atomApplication.launch(parseCommandLine([newFilePath])) await focusWindow(window) const {editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.observeTextEditors(editor => { @@ -324,7 +324,7 @@ describe('AtomApplication', function () { atomApplication.config.set('core.disabledPackages', ['fuzzy-finder']) const remotePath = 'remote://server:3437/some/directory/path' - let window = atomApplication.launch(parseCommandLine([remotePath])) + let [window] = await atomApplication.launch(parseCommandLine([remotePath])) await focusWindow(window) await conditionPromise(async () => (await getProjectDirectories()).length > 0) @@ -350,9 +350,9 @@ describe('AtomApplication', function () { const tempDirPath2 = makeTempDir() const atomApplication1 = buildAtomApplication() - const app1Window1 = atomApplication1.launch(parseCommandLine([tempDirPath1])) + const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1])) await emitterEventPromise(app1Window1, 'window:locations-opened') - const app1Window2 = atomApplication1.launch(parseCommandLine([tempDirPath2])) + const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2])) await emitterEventPromise(app1Window2, 'window:locations-opened') await Promise.all([ @@ -361,7 +361,7 @@ describe('AtomApplication', function () { ]) const atomApplication2 = buildAtomApplication() - const [app2Window1, app2Window2] = atomApplication2.launch(parseCommandLine([])) + const [app2Window1, app2Window2] = await atomApplication2.launch(parseCommandLine([])) await Promise.all([ emitterEventPromise(app2Window1, 'window:locations-opened'), emitterEventPromise(app2Window2, 'window:locations-opened') @@ -373,9 +373,9 @@ describe('AtomApplication', function () { it('does not reopen any previously opened windows when launched with no path and `core.restorePreviousWindowsOnStart` is no', async () => { const atomApplication1 = buildAtomApplication() - const app1Window1 = atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window1] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) await focusWindow(app1Window1) - const app1Window2 = atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window2] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) await focusWindow(app1Window2) const configPath = path.join(process.env.ATOM_HOME, 'config.cson') @@ -385,7 +385,7 @@ describe('AtomApplication', function () { season.writeFileSync(configPath, config) const atomApplication2 = buildAtomApplication() - const app2Window = atomApplication2.launch(parseCommandLine([])) + const [app2Window] = await atomApplication2.launch(parseCommandLine([])) await focusWindow(app2Window) assert.deepEqual(app2Window.representedDirectoryPaths, []) }) @@ -405,10 +405,10 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened window is closed', async () => { - const window1 = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) + const [window1] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) await focusWindow(window1) - const [window2] = atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102'])) + const [window2] = await atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102'])) await focusWindow(window2) assert.deepEqual(killedPids, []) @@ -424,7 +424,7 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened file in an existing window is closed', async () => { - const window = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) + const [window] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) await focusWindow(window) const filePath1 = temp.openSync('test').path @@ -432,7 +432,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath1, 'File 1') fs.writeFileSync(filePath2, 'File 2') - const reusedWindow = atomApplication.launch(parseCommandLine(['--wait', '--pid', '102', filePath1, filePath2])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '102', filePath1, filePath2])) assert.equal(reusedWindow, window) const activeEditorPath = await evalInWebContents(window.browserWindow.webContents, send => { @@ -467,11 +467,11 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened directory in an existing window is closed', async () => { - const window = atomApplication.launch(parseCommandLine([])) + const [window] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window) const dirPath1 = makeTempDir() - const reusedWindow = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', dirPath1])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', dirPath1])) assert.equal(reusedWindow, window) assert.deepEqual(await getTreeViewRootDirectories(window), [dirPath1]) assert.deepEqual(killedPids, []) @@ -498,7 +498,7 @@ describe('AtomApplication', function () { if (process.platform === 'linux' || process.platform === 'win32') { it('quits the application', async () => { const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) + const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) await focusWindow(window) window.close() await window.closedPromise @@ -508,7 +508,7 @@ describe('AtomApplication', function () { } else if (process.platform === 'darwin') { it('leaves the application open', async () => { const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) + const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) await focusWindow(window) window.close() await window.closedPromise @@ -524,7 +524,7 @@ describe('AtomApplication', function () { const dirB = makeTempDir() const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([dirA, dirB])) + const [window] = await atomApplication.launch(parseCommandLine([dirA, dirB])) await emitterEventPromise(window, 'window:locations-opened') await focusWindow(window) assert.deepEqual(await getTreeViewRootDirectories(window), [dirA, dirB]) @@ -539,7 +539,7 @@ describe('AtomApplication', function () { // Window state should be saved when the project folder is removed const atomApplication2 = buildAtomApplication() - const [window2] = atomApplication2.launch(parseCommandLine([])) + const [window2] = await atomApplication2.launch(parseCommandLine([])) await emitterEventPromise(window2, 'window:locations-opened') await focusWindow(window2) assert.deepEqual(await getTreeViewRootDirectories(window2), [dirB]) @@ -556,7 +556,7 @@ describe('AtomApplication', function () { const atomApplication = buildAtomApplication() const launchOptions = parseCommandLine([]) launchOptions.urlsToOpen = ['atom://package-with-url-main/test'] - let windows = atomApplication.launch(launchOptions) + let [windows] = await atomApplication.launch(launchOptions) await windows[0].loadedPromise let reached = await evalInWebContents(windows[0].browserWindow.webContents, sendBackToMainProcess => { @@ -571,9 +571,9 @@ describe('AtomApplication', function () { const dirBPath = makeTempDir('b') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath)])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath)])) await focusWindow(window1) - const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath)])) + const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath)])) await focusWindow(window2) const fileA = path.join(dirAPath, 'file-a') @@ -597,9 +597,9 @@ describe('AtomApplication', function () { const dirAPath = makeTempDir("a") const dirBPath = makeTempDir("b") const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'file-a')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'file-a')])) await focusWindow(window1) - const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath, 'file-b')])) + const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath, 'file-b')])) await focusWindow(window2) electron.app.quit() await new Promise(process.nextTick) @@ -612,8 +612,8 @@ describe('AtomApplication', function () { it('prevents quitting if user cancels when prompted to save an item', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) - const window2 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) + const [window2] = await atomApplication.launch(parseCommandLine([])) await Promise.all([window1.loadedPromise, window2.loadedPromise]) await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.getActiveTextEditor().insertText('unsaved text') diff --git a/spec/main-process/file-recovery-service.test.js b/spec/main-process/file-recovery-service.test.js index 618c30ab0..2a8f2088c 100644 --- a/spec/main-process/file-recovery-service.test.js +++ b/spec/main-process/file-recovery-service.test.js @@ -1,21 +1,21 @@ -/** @babel */ - -import {dialog} from 'electron' -import FileRecoveryService from '../../src/main-process/file-recovery-service' -import fs from 'fs-plus' -import sinon from 'sinon' -import {escapeRegExp} from 'underscore-plus' +const {dialog} = require('electron') +const FileRecoveryService = require('../../src/main-process/file-recovery-service') +const fs = require('fs-plus') +const sinon = require('sinon') +const {escapeRegExp} = require('underscore-plus') const temp = require('temp').track() describe("FileRecoveryService", () => { - let recoveryService, recoveryDirectory + let recoveryService, recoveryDirectory, spies beforeEach(() => { recoveryDirectory = temp.mkdirSync('atom-spec-file-recovery') recoveryService = new FileRecoveryService(recoveryDirectory) + spies = sinon.sandbox.create() }) afterEach(() => { + spies.restore() try { temp.cleanupSync() } catch (e) { @@ -24,38 +24,38 @@ describe("FileRecoveryService", () => { }) describe("when no crash happens during a save", () => { - it("creates a recovery file and deletes it after saving", () => { + it("creates a recovery file and deletes it after saving", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didSavePath(mockWindow, filePath) + await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") fs.removeSync(filePath) }) - it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", () => { + it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didSavePath(mockWindow, filePath) + await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") - recoveryService.didSavePath(anotherMockWindow, filePath) + await recoveryService.didSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") @@ -64,66 +64,66 @@ describe("FileRecoveryService", () => { }) describe("when a crash happens during a save", () => { - it("restores the created recovery file and deletes it", () => { + it("restores the created recovery file and deletes it", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didCrashWindow(mockWindow) + await recoveryService.didCrashWindow(mockWindow) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "some content") fs.removeSync(filePath) }) - it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", () => { + it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "A") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) fs.writeFileSync(filePath, "B") - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "C") - recoveryService.didCrashWindow(mockWindow) + await recoveryService.didCrashWindow(mockWindow) assert.equal(fs.readFileSync(filePath, 'utf8'), "A") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) fs.writeFileSync(filePath, "D") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) fs.writeFileSync(filePath, "E") - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "F") - recoveryService.didCrashWindow(anotherMockWindow) + await recoveryService.didCrashWindow(anotherMockWindow) assert.equal(fs.readFileSync(filePath, 'utf8'), "D") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) fs.removeSync(filePath) }) - it("emits a warning when a file can't be recovered", sinon.test(function () { + it("emits a warning when a file can't be recovered", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "content") fs.chmodSync(filePath, 0444) let logs = [] - this.stub(console, 'log', (message) => logs.push(message)) - this.stub(dialog, 'showMessageBox') + spies.stub(console, 'log', (message) => logs.push(message)) + spies.stub(dialog, 'showMessageBox') - recoveryService.willSavePath(mockWindow, filePath) - recoveryService.didCrashWindow(mockWindow) + await recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.didCrashWindow(mockWindow) let recoveryFiles = fs.listTreeSync(recoveryDirectory) assert.equal(recoveryFiles.length, 1) assert.equal(logs.length, 1) @@ -131,16 +131,16 @@ describe("FileRecoveryService", () => { assert.match(logs[0], new RegExp(escapeRegExp(recoveryFiles[0]))) fs.removeSync(filePath) - })) + }) }) - it("doesn't create a recovery file when the file that's being saved doesn't exist yet", () => { + it("doesn't create a recovery file when the file that's being saved doesn't exist yet", async () => { const mockWindow = {} - recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) }) }) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index c744ce795..3432a0fea 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -104,7 +104,7 @@ describe('TextEditorComponent', () => { { expect(editor.getApproximateLongestScreenRow()).toBe(3) - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + component.getBaseCharacterWidth() ) @@ -121,7 +121,7 @@ describe('TextEditorComponent', () => { // Capture the width of the lines before requesting the width of // longest line, because making that request forces a DOM update const actualWidth = element.querySelector('.lines').style.width - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(6, Infinity)).left + component.getBaseCharacterWidth() ) @@ -3980,7 +3980,7 @@ describe('TextEditorComponent', () => { // Capture the width of the lines before requesting the width of // longest line, because making that request forces a DOM update const actualWidth = element.querySelector('.lines').style.width - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + component.getBaseCharacterWidth() ) diff --git a/src/application-delegate.js b/src/application-delegate.js index a6d701078..1cb0c740a 100644 --- a/src/application-delegate.js +++ b/src/application-delegate.js @@ -354,11 +354,11 @@ class ApplicationDelegate { } emitWillSavePath (path) { - return ipcRenderer.sendSync('will-save-path', path) + return ipcHelpers.call('will-save-path', path) } emitDidSavePath (path) { - return ipcRenderer.sendSync('did-save-path', path) + return ipcHelpers.call('did-save-path', path) } resolveProxy (requestId, url) { diff --git a/src/atom-environment.js b/src/atom-environment.js index a80cde66c..8ac507258 100644 --- a/src/atom-environment.js +++ b/src/atom-environment.js @@ -9,7 +9,6 @@ const fs = require('fs-plus') const {mapSourcePosition} = require('@atom/source-map-support') const WindowEventHandler = require('./window-event-handler') const StateStore = require('./state-store') -const StorageFolder = require('./storage-folder') const registerDefaultCommands = require('./register-default-commands') const {updateProcessEnv} = require('./update-process-env') const ConfigSchema = require('./config-schema') @@ -208,12 +207,7 @@ class AtomEnvironment { this.blobStore = params.blobStore this.configDirPath = params.configDirPath - const {devMode, safeMode, resourcePath, clearWindowState} = this.getLoadSettings() - - if (clearWindowState) { - this.getStorageFolder().clear() - this.stateStore.clear() - } + const {devMode, safeMode, resourcePath} = this.getLoadSettings() ConfigSchema.projectHome = { type: 'string', @@ -764,7 +758,11 @@ class AtomEnvironment { } // Call this method when establishing a real application window. - startEditorWindow () { + async startEditorWindow () { + if (this.getLoadSettings().clearWindowState) { + await this.stateStore.clear() + } + this.unloaded = false const updateProcessEnvPromise = this.updateProcessEnvAndTriggerHooks() @@ -1264,11 +1262,6 @@ or use Pane::saveItemAs for programmatic saving.`) } } - getStorageFolder () { - if (!this.storageFolder) this.storageFolder = new StorageFolder(this.getConfigDirPath()) - return this.storageFolder - } - getConfigDirPath () { if (!this.configDirPath) this.configDirPath = process.env.ATOM_HOME return this.configDirPath diff --git a/src/atom-paths.js b/src/atom-paths.js index 39a768e91..d36ac25f5 100644 --- a/src/atom-paths.js +++ b/src/atom-paths.js @@ -1,5 +1,3 @@ -/** @babel */ - const fs = require('fs-plus') const path = require('path') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 111147f32..5e3a80129 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -1,8 +1,7 @@ -'use babel' +const {Emitter, CompositeDisposable} = require('event-kit') -import {Emitter, CompositeDisposable} from 'event-kit' - -export default class AutoUpdateManager { +module.exports = +class AutoUpdateManager { constructor ({applicationDelegate}) { this.applicationDelegate = applicationDelegate this.subscriptions = new CompositeDisposable() diff --git a/src/buffered-node-process.js b/src/buffered-node-process.js index 86b0c5747..a33176e51 100644 --- a/src/buffered-node-process.js +++ b/src/buffered-node-process.js @@ -1,6 +1,4 @@ -/** @babel */ - -import BufferedProcess from './buffered-process' +const BufferedProcess = require('./buffered-process') // Extended: Like {BufferedProcess}, but accepts a Node script as the command // to run. @@ -12,7 +10,8 @@ import BufferedProcess from './buffered-process' // ```js // const {BufferedNodeProcess} = require('atom') // ``` -export default class BufferedNodeProcess extends BufferedProcess { +module.exports = +class BufferedNodeProcess extends BufferedProcess { // Public: Runs the given Node script by spawning a new child process. // diff --git a/src/buffered-process.js b/src/buffered-process.js index 339bf05c5..0a01671fa 100644 --- a/src/buffered-process.js +++ b/src/buffered-process.js @@ -1,9 +1,7 @@ -/** @babel */ - -import _ from 'underscore-plus' -import ChildProcess from 'child_process' -import {Emitter} from 'event-kit' -import path from 'path' +const _ = require('underscore-plus') +const ChildProcess = require('child_process') +const {Emitter} = require('event-kit') +const path = require('path') // Extended: A wrapper which provides standard error/output line buffering for // Node's ChildProcess. @@ -19,7 +17,8 @@ import path from 'path' // const exit = (code) => console.log("ps -ef exited with #{code}") // const process = new BufferedProcess({command, args, stdout, exit}) // ``` -export default class BufferedProcess { +module.exports = +class BufferedProcess { /* Section: Construction */ diff --git a/src/clipboard.js b/src/clipboard.js index 34f6b1f83..d36bb1018 100644 --- a/src/clipboard.js +++ b/src/clipboard.js @@ -1,7 +1,5 @@ -/** @babel */ - -import crypto from 'crypto' -import clipboard from './safe-clipboard' +const crypto = require('crypto') +const clipboard = require('./safe-clipboard') // Extended: Represents the clipboard used for copying and pasting in Atom. // @@ -14,7 +12,8 @@ import clipboard from './safe-clipboard' // // console.log(atom.clipboard.read()) # 'hello' // ``` -export default class Clipboard { +module.exports = +class Clipboard { constructor () { this.reset() } diff --git a/src/color.js b/src/color.js index 52f555076..c183fb3e5 100644 --- a/src/color.js +++ b/src/color.js @@ -1,10 +1,9 @@ -/** @babel */ - let ParsedColor = null // Essential: A simple color class returned from {Config::get} when the value // at the key path is of type 'color'. -export default class Color { +module.exports = +class Color { // Essential: Parse a {String} or {Object} into a {Color}. // // * `value` A {String} such as `'white'`, `#ff00ff`, or diff --git a/src/deserializer-manager.js b/src/deserializer-manager.js index a11acc319..72ed9485d 100644 --- a/src/deserializer-manager.js +++ b/src/deserializer-manager.js @@ -1,6 +1,4 @@ -/** @babel */ - -import {Disposable} from 'event-kit' +const {Disposable} = require('event-kit') // Extended: Manages the deserializers used for serialized state // @@ -21,7 +19,8 @@ import {Disposable} from 'event-kit' // serialize: -> // @state // ``` -export default class DeserializerManager { +module.exports = +class DeserializerManager { constructor (atomEnvironment) { this.atomEnvironment = atomEnvironment this.deserializers = {} diff --git a/src/history-manager.js b/src/history-manager.js index 306c11812..e4651d9d9 100644 --- a/src/history-manager.js +++ b/src/history-manager.js @@ -1,13 +1,11 @@ -/** @babel */ - -import {Emitter, CompositeDisposable} from 'event-kit' +const {Emitter, CompositeDisposable} = require('event-kit') // Extended: History manager for remembering which projects have been opened. // // An instance of this class is always available as the `atom.history` global. // // The project history is used to enable the 'Reopen Project' menu. -export class HistoryManager { +class HistoryManager { constructor ({project, commands, stateStore}) { this.stateStore = stateStore this.emitter = new Emitter() @@ -116,7 +114,7 @@ function arrayEquivalent (a, b) { return true } -export class HistoryProject { +class HistoryProject { constructor (paths, lastOpened) { this.paths = paths this.lastOpened = lastOpened || new Date() @@ -128,3 +126,5 @@ export class HistoryProject { set lastOpened (lastOpened) { this._lastOpened = lastOpened } get lastOpened () { return this._lastOpened } } + +module.exports = {HistoryManager, HistoryProject} diff --git a/src/initialize-benchmark-window.js b/src/initialize-benchmark-window.js index 7ba99c468..131785454 100644 --- a/src/initialize-benchmark-window.js +++ b/src/initialize-benchmark-window.js @@ -1,11 +1,9 @@ -/** @babel */ +const {remote} = require('electron') +const path = require('path') +const ipcHelpers = require('./ipc-helpers') +const util = require('util') -import {remote} from 'electron' -import path from 'path' -import ipcHelpers from './ipc-helpers' -import util from 'util' - -export default async function () { +module.exports = async function () { const getWindowLoadSettings = require('./get-window-load-settings') const {test, headless, resourcePath, benchmarkPaths} = getWindowLoadSettings() try { diff --git a/src/ipc-helpers.js b/src/ipc-helpers.js index 4be9f9613..b68877f99 100644 --- a/src/ipc-helpers.js +++ b/src/ipc-helpers.js @@ -1,15 +1,13 @@ -'use strict' - const Disposable = require('event-kit').Disposable let ipcRenderer = null let ipcMain = null let BrowserWindow = null +let nextResponseChannelId = 0 + exports.on = function (emitter, eventName, callback) { emitter.on(eventName, callback) - return new Disposable(function () { - emitter.removeListener(eventName, callback) - }) + return new Disposable(() => emitter.removeListener(eventName, callback)) } exports.call = function (channel, ...args) { @@ -18,34 +16,28 @@ exports.call = function (channel, ...args) { ipcRenderer.setMaxListeners(20) } - var responseChannel = getResponseChannel(channel) + const responseChannel = `ipc-helpers-response-${nextResponseChannelId++}` - return new Promise(function (resolve) { - ipcRenderer.on(responseChannel, function (event, result) { + return new Promise(resolve => { + ipcRenderer.on(responseChannel, (event, result) => { ipcRenderer.removeAllListeners(responseChannel) resolve(result) }) - ipcRenderer.send(channel, ...args) + ipcRenderer.send(channel, responseChannel, ...args) }) } exports.respondTo = function (channel, callback) { if (!ipcMain) { - var electron = require('electron') + const electron = require('electron') ipcMain = electron.ipcMain BrowserWindow = electron.BrowserWindow } - var responseChannel = getResponseChannel(channel) - - return exports.on(ipcMain, channel, function (event, ...args) { - var browserWindow = BrowserWindow.fromWebContents(event.sender) - var result = callback(browserWindow, ...args) + return exports.on(ipcMain, channel, async (event, responseChannel, ...args) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender) + const result = await callback(browserWindow, ...args) event.sender.send(responseChannel, result) }) } - -function getResponseChannel (channel) { - return 'ipc-helpers-' + channel + '-response' -} diff --git a/src/main-process/atom-application.js b/src/main-process/atom-application.js index 372bd537c..49c691b66 100644 --- a/src/main-process/atom-application.js +++ b/src/main-process/atom-application.js @@ -169,18 +169,32 @@ class AtomApplication extends EventEmitter { this.disposable.dispose() } - launch (options) { + async launch (options) { + const optionsForWindowsToOpen = [] + + let shouldReopenPreviousWindows = false + if (options.test || options.benchmark || options.benchmarkTest) { - return this.openWithOptions(options) + optionsForWindowsToOpen.push(options) } else if ((options.pathsToOpen && options.pathsToOpen.length > 0) || (options.urlsToOpen && options.urlsToOpen.length > 0)) { - if (this.config.get('core.restorePreviousWindowsOnStart') === 'always') { - this.loadState(_.deepClone(options)) - } - return this.openWithOptions(options) + optionsForWindowsToOpen.push(options) + shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') === 'always' } else { - return this.loadState(options) || this.openPath(options) + shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') !== 'no' } + + if (shouldReopenPreviousWindows) { + for (const previousOptions of await this.loadPreviousWindowOptions()) { + optionsForWindowsToOpen.push(Object.assign({}, options, previousOptions)) + } + } + + if (optionsForWindowsToOpen.length === 0) { + optionsForWindowsToOpen.push(options) + } + + return optionsForWindowsToOpen.map(options => this.openWithOptions(options)) } openWithOptions (options) { @@ -271,7 +285,7 @@ class AtomApplication extends EventEmitter { return } } - if (!window.isSpec) this.saveState(true) + if (!window.isSpec) this.saveCurrentWindowOptions(true) } // Public: Adds the {AtomWindow} to the global window list. @@ -285,7 +299,7 @@ class AtomApplication extends EventEmitter { if (!window.isSpec) { const focusHandler = () => this.windowStack.touch(window) - const blurHandler = () => this.saveState(false) + const blurHandler = () => this.saveCurrentWindowOptions(false) window.browserWindow.on('focus', focusHandler) window.browserWindow.on('blur', blurHandler) window.browserWindow.once('closed', () => { @@ -569,18 +583,16 @@ class AtomApplication extends EventEmitter { event.returnValue = this.autoUpdateManager.getErrorMessage() })) - this.disposable.add(ipcHelpers.on(ipcMain, 'will-save-path', (event, path) => { - this.fileRecoveryService.willSavePath(this.atomWindowForEvent(event), path) - event.returnValue = true - })) + this.disposable.add(ipcHelpers.respondTo('will-save-path', (window, path) => + this.fileRecoveryService.willSavePath(window, path) + )) - this.disposable.add(ipcHelpers.on(ipcMain, 'did-save-path', (event, path) => { - this.fileRecoveryService.didSavePath(this.atomWindowForEvent(event), path) - event.returnValue = true - })) + this.disposable.add(ipcHelpers.respondTo('did-save-path', (window, path) => + this.fileRecoveryService.didSavePath(window, path) + )) this.disposable.add(ipcHelpers.on(ipcMain, 'did-change-paths', () => - this.saveState(false) + this.saveCurrentWindowOptions(false) )) this.disposable.add(this.disableZoomOnDisplayChange()) @@ -911,7 +923,7 @@ class AtomApplication extends EventEmitter { } } - saveState (allowEmpty = false) { + async saveCurrentWindowOptions (allowEmpty = false) { if (this.quitting) return const states = [] @@ -921,28 +933,23 @@ class AtomApplication extends EventEmitter { states.reverse() if (states.length > 0 || allowEmpty) { - this.storageFolder.storeSync('application.json', states) + await this.storageFolder.store('application.json', states) this.emit('application:did-save-state') } } - loadState (options) { - const states = this.storageFolder.load('application.json') - if ( - ['yes', 'always'].includes(this.config.get('core.restorePreviousWindowsOnStart')) && - states && states.length > 0 - ) { - return states.map(state => - this.openWithOptions(Object.assign(options, { - initialPaths: state.initialPaths, - pathsToOpen: state.initialPaths.filter(p => fs.isDirectorySync(p)), - urlsToOpen: [], - devMode: this.devMode, - safeMode: this.safeMode - })) - ) + async loadPreviousWindowOptions () { + const states = await this.storageFolder.load('application.json') + if (states) { + return states.map(state => ({ + initialPaths: state.initialPaths, + pathsToOpen: state.initialPaths.filter(p => fs.isDirectorySync(p)), + urlsToOpen: [], + devMode: this.devMode, + safeMode: this.safeMode + })) } else { - return null + return [] } } @@ -1293,17 +1300,16 @@ class AtomApplication extends EventEmitter { // File dialog defaults to project directory of currently active editor if (path) openOptions.defaultPath = path - return dialog.showOpenDialog(parentWindow, openOptions, callback) + dialog.showOpenDialog(parentWindow, openOptions, callback) } promptForRestart () { - const chosen = dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { + dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { type: 'warning', title: 'Restart required', message: 'You will need to restart Atom for this change to take effect.', buttons: ['Restart Atom', 'Cancel'] - }) - if (chosen === 0) return this.restart() + }, response => { if (response === 0) this.restart() }) } restart () { diff --git a/src/main-process/atom-window.js b/src/main-process/atom-window.js index 0268cc1cf..dd7994e89 100644 --- a/src/main-process/atom-window.js +++ b/src/main-process/atom-window.js @@ -163,7 +163,7 @@ class AtomWindow extends EventEmitter { if (!this.atomApplication.quitting && !this.unloading) { event.preventDefault() this.unloading = true - this.atomApplication.saveState(false) + this.atomApplication.saveCurrentWindowOptions(false) if (await this.prepareToUnload()) this.close() } }) @@ -176,34 +176,34 @@ class AtomWindow extends EventEmitter { this.browserWindow.on('unresponsive', () => { if (this.isSpec) return - const chosen = dialog.showMessageBox(this.browserWindow, { + dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Force Close', 'Keep Waiting'], message: 'Editor is not responding', detail: 'The editor is not responding. Would you like to force close it or just keep waiting?' - }) - if (chosen === 0) this.browserWindow.destroy() + }, response => { if (response === 0) this.browserWindow.destroy() }) }) - this.browserWindow.webContents.on('crashed', () => { + this.browserWindow.webContents.on('crashed', async () => { if (this.headless) { console.log('Renderer process crashed, exiting') this.atomApplication.exit(100) return } - this.fileRecoveryService.didCrashWindow(this) - const chosen = dialog.showMessageBox(this.browserWindow, { + await this.fileRecoveryService.didCrashWindow(this) + dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Close Window', 'Reload', 'Keep It Open'], message: 'The editor has crashed', detail: 'Please report this issue to https://github.com/atom/atom' + }, response => { + switch (response) { + case 0: return this.browserWindow.destroy() + case 1: return this.browserWindow.reload() + } }) - switch (chosen) { - case 0: return this.browserWindow.destroy() - case 1: return this.browserWindow.reload() - } }) this.browserWindow.webContents.on('will-navigate', (event, url) => { @@ -415,7 +415,7 @@ class AtomWindow extends EventEmitter { this.representedDirectoryPaths.sort() this.loadSettings.initialPaths = this.representedDirectoryPaths this.browserWindow.loadSettingsJSON = JSON.stringify(this.loadSettings) - return this.atomApplication.saveState() + return this.atomApplication.saveCurrentWindowOptions() } didClosePathWithWaitSession (path) { diff --git a/src/main-process/auto-update-manager.coffee b/src/main-process/auto-update-manager.coffee index 0e4144c1a..bc81d425d 100644 --- a/src/main-process/auto-update-manager.coffee +++ b/src/main-process/auto-update-manager.coffee @@ -118,24 +118,26 @@ class AutoUpdateManager onUpdateNotAvailable: => autoUpdater.removeListener 'error', @onUpdateError {dialog} = require 'electron' - dialog.showMessageBox + dialog.showMessageBox { type: 'info' buttons: ['OK'] icon: @iconPath message: 'No update available.' title: 'No Update Available' detail: "Version #{@version} is the latest version." + }, -> # noop callback to get async behavior onUpdateError: (event, message) => autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable {dialog} = require 'electron' - dialog.showMessageBox + dialog.showMessageBox { type: 'warning' buttons: ['OK'] icon: @iconPath message: 'There was an error checking for updates.' title: 'Update Error' detail: message + }, -> # noop callback to get async behavior getWindows: -> global.atomApplication.getAllWindows() diff --git a/src/main-process/file-recovery-service.js b/src/main-process/file-recovery-service.js index f55e3f956..eef84089d 100644 --- a/src/main-process/file-recovery-service.js +++ b/src/main-process/file-recovery-service.js @@ -1,11 +1,10 @@ -'use babel' +const {dialog} = require('electron') +const crypto = require('crypto') +const Path = require('path') +const fs = require('fs-plus') -import {dialog} from 'electron' -import crypto from 'crypto' -import Path from 'path' -import fs from 'fs-plus' - -export default class FileRecoveryService { +module.exports = +class FileRecoveryService { constructor (recoveryDirectory) { this.recoveryDirectory = recoveryDirectory this.recoveryFilesByFilePath = new Map() @@ -13,15 +12,16 @@ export default class FileRecoveryService { this.windowsByRecoveryFile = new Map() } - willSavePath (window, path) { - if (!fs.existsSync(path)) return + async willSavePath (window, path) { + const stats = await tryStatFile(path) + if (!stats) return const recoveryPath = Path.join(this.recoveryDirectory, RecoveryFile.fileNameForPath(path)) const recoveryFile = - this.recoveryFilesByFilePath.get(path) || new RecoveryFile(path, recoveryPath) + this.recoveryFilesByFilePath.get(path) || new RecoveryFile(path, stats.mode, recoveryPath) try { - recoveryFile.retain() + await recoveryFile.retain() } catch (err) { console.log(`Couldn't retain ${recoveryFile.recoveryPath}. Code: ${err.code}. Message: ${err.message}`) return @@ -39,11 +39,11 @@ export default class FileRecoveryService { this.recoveryFilesByFilePath.set(path, recoveryFile) } - didSavePath (window, path) { + async didSavePath (window, path) { const recoveryFile = this.recoveryFilesByFilePath.get(path) if (recoveryFile != null) { try { - recoveryFile.release() + await recoveryFile.release() } catch (err) { console.log(`Couldn't release ${recoveryFile.recoveryPath}. Code: ${err.code}. Message: ${err.message}`) } @@ -53,27 +53,31 @@ export default class FileRecoveryService { } } - didCrashWindow (window) { + async didCrashWindow (window) { if (!this.recoveryFilesByWindow.has(window)) return + const promises = [] for (const recoveryFile of this.recoveryFilesByWindow.get(window)) { - try { - recoveryFile.recoverSync() - } catch (error) { - const message = 'A file that Atom was saving could be corrupted' - const detail = - `Error ${error.code}. There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + - `Atom couldn't recover it automatically, but a recovery file has been saved at: "${recoveryFile.recoveryPath}".` - console.log(detail) - dialog.showMessageBox(window.browserWindow, {type: 'info', buttons: ['OK'], message, detail}) - } finally { - for (let window of this.windowsByRecoveryFile.get(recoveryFile)) { - this.recoveryFilesByWindow.get(window).delete(recoveryFile) - } - this.windowsByRecoveryFile.delete(recoveryFile) - this.recoveryFilesByFilePath.delete(recoveryFile.originalPath) - } + promises.push(recoveryFile.recover() + .catch(error => { + const message = 'A file that Atom was saving could be corrupted' + const detail = + `Error ${error.code}. There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + + `Atom couldn't recover it automatically, but a recovery file has been saved at: "${recoveryFile.recoveryPath}".` + console.log(detail) + dialog.showMessageBox(window, {type: 'info', buttons: ['OK'], message, detail}, () => { /* noop callback to get async behavior */ }) + }) + .then(() => { + for (let window of this.windowsByRecoveryFile.get(recoveryFile)) { + this.recoveryFilesByWindow.get(window).delete(recoveryFile) + } + this.windowsByRecoveryFile.delete(recoveryFile) + this.recoveryFilesByFilePath.delete(recoveryFile.originalPath) + }) + ) } + + await Promise.all(promises) } didCloseWindow (window) { @@ -94,36 +98,64 @@ class RecoveryFile { return `${basename}-${randomSuffix}${extension}` } - constructor (originalPath, recoveryPath) { + constructor (originalPath, fileMode, recoveryPath) { this.originalPath = originalPath + this.fileMode = fileMode this.recoveryPath = recoveryPath this.refCount = 0 } - storeSync () { - fs.copyFileSync(this.originalPath, this.recoveryPath) + async store () { + await copyFile(this.originalPath, this.recoveryPath, this.fileMode) } - recoverSync () { - fs.copyFileSync(this.recoveryPath, this.originalPath) - this.removeSync() + async recover () { + await copyFile(this.recoveryPath, this.originalPath, this.fileMode) + await this.remove() } - removeSync () { - fs.unlinkSync(this.recoveryPath) + async remove () { + return new Promise((resolve, reject) => + fs.unlink(this.recoveryPath, error => + error && error.code !== 'ENOENT' ? reject(error) : resolve() + ) + ) } - retain () { - if (this.isReleased()) this.storeSync() + async retain () { + if (this.isReleased()) await this.store() this.refCount++ } - release () { + async release () { this.refCount-- - if (this.isReleased()) this.removeSync() + if (this.isReleased()) await this.remove() } isReleased () { return this.refCount === 0 } } + +async function tryStatFile (path) { + return new Promise((resolve, reject) => + fs.stat(path, (error, result) => + resolve(error == null && result) + ) + ) +} + +async function copyFile (source, destination, mode) { + return new Promise((resolve, reject) => { + const readStream = fs.createReadStream(source) + readStream + .on('error', reject) + .once('open', () => { + const writeStream = fs.createWriteStream(destination, {mode}) + writeStream + .on('error', reject) + .on('open', () => readStream.pipe(writeStream)) + .once('close', () => resolve()) + }) + }) +} diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index 9670936c7..dd694b9dd 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -1,7 +1,5 @@ -'use babel' - -import Registry from 'winreg' -import Path from 'path' +const Registry = require('winreg') +const Path = require('path') let exeName = Path.basename(process.execPath) let appPath = `\"${process.execPath}\"` diff --git a/src/native-watcher-registry.js b/src/native-watcher-registry.js index e63ac6cda..97f33e3fb 100644 --- a/src/native-watcher-registry.js +++ b/src/native-watcher-registry.js @@ -1,5 +1,3 @@ -/** @babel */ - const path = require('path') // Private: re-join the segments split from an absolute path to form another absolute path. diff --git a/src/null-grammar.js b/src/null-grammar.js index fe9c3889e..12cfbbe53 100644 --- a/src/null-grammar.js +++ b/src/null-grammar.js @@ -1,8 +1,6 @@ -/** @babel */ +const {Disposable} = require('event-kit') -import {Disposable} from 'event-kit' - -export default { +module.exports = { name: 'Null Grammar', scopeName: 'text.plain.null-grammar', scopeForId (id) { diff --git a/src/path-watcher.js b/src/path-watcher.js index d0ff90dd1..ff7e8fd56 100644 --- a/src/path-watcher.js +++ b/src/path-watcher.js @@ -1,5 +1,3 @@ -/** @babel */ - const fs = require('fs') const path = require('path') diff --git a/src/project.js b/src/project.js index 18e71c915..9cd2d1245 100644 --- a/src/project.js +++ b/src/project.js @@ -695,7 +695,7 @@ class Project extends Model { } subscribeToBuffer (buffer) { - buffer.onWillSave(({path}) => this.applicationDelegate.emitWillSavePath(path)) + buffer.onWillSave(async ({path}) => this.applicationDelegate.emitWillSavePath(path)) buffer.onDidSave(({path}) => this.applicationDelegate.emitDidSavePath(path)) buffer.onDidDestroy(() => this.removeBuffer(buffer)) buffer.onDidChangePath(() => { diff --git a/src/reopen-project-list-view.js b/src/reopen-project-list-view.js index f08ee725a..d59577684 100644 --- a/src/reopen-project-list-view.js +++ b/src/reopen-project-list-view.js @@ -1,8 +1,7 @@ -/** @babel */ +const SelectListView = require('atom-select-list') -import SelectListView from 'atom-select-list' - -export default class ReopenProjectListView { +module.exports = +class ReopenProjectListView { constructor (callback) { this.callback = callback this.selectListView = new SelectListView({ diff --git a/src/reopen-project-menu-manager.js b/src/reopen-project-menu-manager.js index 3f88e41f0..35564f705 100644 --- a/src/reopen-project-menu-manager.js +++ b/src/reopen-project-menu-manager.js @@ -1,9 +1,8 @@ -/** @babel */ +const {CompositeDisposable} = require('event-kit') +const path = require('path') -import {CompositeDisposable} from 'event-kit' -import path from 'path' - -export default class ReopenProjectMenuManager { +module.exports = +class ReopenProjectMenuManager { constructor ({menu, commands, history, config, open}) { this.menuManager = menu this.historyManager = history diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee deleted file mode 100644 index 280eb8b5c..000000000 --- a/src/storage-folder.coffee +++ /dev/null @@ -1,39 +0,0 @@ -path = require "path" -fs = require "fs-plus" - -module.exports = -class StorageFolder - constructor: (containingPath) -> - @path = path.join(containingPath, "storage") if containingPath? - - clear: -> - return unless @path? - - try - fs.removeSync(@path) - catch error - console.warn "Error deleting #{@path}", error.stack, error - - storeSync: (name, object) -> - return unless @path? - - fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') - - load: (name) -> - return unless @path? - - statePath = @pathForKey(name) - try - stateString = fs.readFileSync(statePath, 'utf8') - catch error - unless error.code is 'ENOENT' - console.warn "Error reading state file: #{statePath}", error.stack, error - return undefined - - try - JSON.parse(stateString) - catch error - console.warn "Error parsing state file: #{statePath}", error.stack, error - - pathForKey: (name) -> path.join(@getPath(), name) - getPath: -> @path diff --git a/src/storage-folder.js b/src/storage-folder.js new file mode 100644 index 000000000..4931dab11 --- /dev/null +++ b/src/storage-folder.js @@ -0,0 +1,49 @@ +const path = require('path') +const fs = require('fs-plus') + +module.exports = +class StorageFolder { + constructor (containingPath) { + if (containingPath) { + this.path = path.join(containingPath, 'storage') + } + } + + store (name, object) { + return new Promise((resolve, reject) => { + if (!this.path) return resolve() + fs.writeFile(this.pathForKey(name), JSON.stringify(object), 'utf8', error => + error ? reject(error) : resolve() + ) + }) + } + + load (name) { + return new Promise(resolve => { + if (!this.path) return resolve(null) + const statePath = this.pathForKey(name) + fs.readFile(statePath, 'utf8', (error, stateString) => { + if (error && error.code !== 'ENOENT') { + console.warn(`Error reading state file: ${statePath}`, error.stack, error) + } + + if (!stateString) return resolve(null) + + try { + resolve(JSON.parse(stateString)) + } catch (error) { + console.warn(`Error parsing state file: ${statePath}`, error.stack, error) + resolve(null) + } + }) + }) + } + + pathForKey (name) { + return path.join(this.getPath(), name) + } + + getPath () { + return this.path + } +} diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 855920b3b..c88aab304 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -2694,7 +2694,7 @@ class TextEditorComponent { } getContentWidth () { - return Math.round(this.getLongestLineWidth() + this.getBaseCharacterWidth()) + return Math.ceil(this.getLongestLineWidth() + this.getBaseCharacterWidth()) } getScrollContainerClientWidthInBaseCharacters () { diff --git a/src/update-process-env.js b/src/update-process-env.js index 00bb13927..6dab00a7d 100644 --- a/src/update-process-env.js +++ b/src/update-process-env.js @@ -1,7 +1,5 @@ -/** @babel */ - -import fs from 'fs' -import childProcess from 'child_process' +const fs = require('fs') +const childProcess = require('child_process') const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set([ 'NODE_ENV', @@ -120,4 +118,4 @@ async function getEnvFromShell (env) { return result } -export default { updateProcessEnv, shouldGetEnvFromShell } +module.exports = {updateProcessEnv, shouldGetEnvFromShell} diff --git a/src/workspace.js b/src/workspace.js index 66e7f8ba5..de51651ec 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -1,5 +1,3 @@ -'use babel' - const _ = require('underscore-plus') const url = require('url') const path = require('path')