Merge branch 'master' of github.com:atom/atom into fb-pw-decaffeinate-config

This commit is contained in:
Philip Weiss
2018-01-22 09:57:57 -08:00
35 changed files with 344 additions and 338 deletions

View File

@@ -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) {

View File

@@ -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

View File

@@ -1,5 +1,3 @@
/** @babel */
const fs = require('fs-plus')
const path = require('path')

View File

@@ -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()

View File

@@ -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.
//

View File

@@ -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
*/

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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 = {}

View File

@@ -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}

View File

@@ -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 {

View File

@@ -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'
}

View File

@@ -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 () {

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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())
})
})
}

View File

@@ -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}\"`

View File

@@ -1,5 +1,3 @@
/** @babel */
const path = require('path')
// Private: re-join the segments split from an absolute path to form another absolute path.

View File

@@ -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) {

View File

@@ -1,5 +1,3 @@
/** @babel */
const fs = require('fs')
const path = require('path')

View File

@@ -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(() => {

View File

@@ -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({

View File

@@ -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

View File

@@ -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

49
src/storage-folder.js Normal file
View File

@@ -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
}
}

View File

@@ -2694,7 +2694,7 @@ class TextEditorComponent {
}
getContentWidth () {
return Math.round(this.getLongestLineWidth() + this.getBaseCharacterWidth())
return Math.ceil(this.getLongestLineWidth() + this.getBaseCharacterWidth())
}
getScrollContainerClientWidthInBaseCharacters () {

View File

@@ -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}

View File

@@ -1,5 +1,3 @@
'use babel'
const _ = require('underscore-plus')
const url = require('url')
const path = require('path')