Merge branch 'master' into ns-mb-detangle-editor

This commit is contained in:
Max Brunsfeld
2016-07-28 10:33:23 -07:00
32 changed files with 577 additions and 398 deletions

View File

@@ -786,9 +786,10 @@ class Config
rootSchema = properties[key]
Object.assign rootSchema, schema
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
@setScopedDefaultsFromSchema(keyPath, schema)
@resetSettingsForSchemaChange()
@transact =>
@setDefaults(keyPath, @extractDefaultsFromSchema(schema))
@setScopedDefaultsFromSchema(keyPath, schema)
@resetSettingsForSchemaChange()
load: ->
@initializeConfigDirectory()
@@ -958,9 +959,10 @@ class Config
setDefaults: (keyPath, defaults) ->
if defaults? and isPlainObject(defaults)
keys = splitKeyPath(keyPath)
for key, childValue of defaults
continue unless defaults.hasOwnProperty(key)
@setDefaults(keys.concat([key]).join('.'), childValue)
@transact =>
for key, childValue of defaults
continue unless defaults.hasOwnProperty(key)
@setDefaults(keys.concat([key]).join('.'), childValue)
else
try
defaults = @makeValueConformToSchema(keyPath, defaults)

View File

@@ -1,13 +0,0 @@
module.exports = (extra) ->
# Breakpad on Mac OS X must be running on UI and non-UI processes
# Crashpad on Windows and Linux should only be running on non-UI process
return if process.type is 'renderer' and process.platform isnt 'darwin'
{crashReporter} = require 'electron'
crashReporter.start({
productName: 'Atom',
companyName: 'GitHub',
submitURL: 'http://54.249.141.255:1127/post'
extra: extra
})

View File

@@ -0,0 +1,10 @@
module.exports = function (extra) {
const {crashReporter} = require('electron')
crashReporter.start({
productName: 'Atom',
companyName: 'GitHub',
submitURL: 'https://crashreporter.atom.io',
autoSubmit: false,
extra: extra
})
}

View File

@@ -1,35 +0,0 @@
fs = require 'fs-plus'
path = require 'path'
{ipcMain} = require 'electron'
module.exports =
class AtomPortable
@getPortableAtomHomePath: ->
execDirectoryPath = path.dirname(process.execPath)
path.join(execDirectoryPath, '..', '.atom')
@setPortable: (existingAtomHome) ->
fs.copySync(existingAtomHome, @getPortableAtomHomePath())
@isPortableInstall: (platform, environmentAtomHome, defaultHome) ->
return false unless platform in ['linux', 'win32']
return false if environmentAtomHome
return false if not fs.existsSync(@getPortableAtomHomePath())
# currently checking only that the directory exists and is writable,
# probably want to do some integrity checks on contents in future
@isPortableAtomHomePathWritable(defaultHome)
@isPortableAtomHomePathWritable: (defaultHome) ->
writable = false
message = ""
try
writePermissionTestFile = path.join(@getPortableAtomHomePath(), "write.test")
fs.writeFileSync(writePermissionTestFile, "test") if not fs.existsSync(writePermissionTestFile)
fs.removeSync(writePermissionTestFile)
writable = true
catch error
message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead (#{defaultHome}). #{error.message}"
ipcMain.on 'check-portable-home-writable', (event) ->
event.sender.send 'check-portable-home-writable-response', {writable, message}
writable

View File

@@ -0,0 +1,58 @@
const fs = require('fs-plus')
const path = require('path')
const {ipcMain} = require('electron')
module.exports = class AtomPortable {
static getPortableAtomHomePath () {
const execDirectoryPath = path.dirname(process.execPath)
return path.join(execDirectoryPath, '..', '.atom')
}
static setPortable (existingAtomHome) {
fs.copySync(existingAtomHome, this.getPortableAtomHomePath())
}
static isPortableInstall (platform, environmentAtomHome, defaultHome) {
if (!['linux', 'win32'].includes(platform)) {
return false
}
if (environmentAtomHome) {
return false
}
if (!fs.existsSync(this.getPortableAtomHomePath())) {
return false
}
// Currently checking only that the directory exists and is writable,
// probably want to do some integrity checks on contents in future.
return this.isPortableAtomHomePathWritable(defaultHome)
}
static isPortableAtomHomePathWritable (defaultHome) {
let writable = false
let message = ''
try {
const writePermissionTestFile = path.join(this.getPortableAtomHomePath(), 'write.test')
if (!fs.existsSync(writePermissionTestFile)) {
fs.writeFileSync(writePermissionTestFile, 'test')
}
fs.removeSync(writePermissionTestFile)
writable = true
} catch (error) {
message = `Failed to use portable Atom home directory (${this.getPortableAtomHomePath()}). Using the default instead (${defaultHome}). ${error.message}.`
}
ipcMain.on('check-portable-home-writable', function (event) {
event.sender.send('check-portable-home-writable-response', {
writable: writable,
message: message
})
})
return writable
}
}

View File

@@ -1,198 +0,0 @@
global.shellStartTime = Date.now()
process.on 'uncaughtException', (error={}) ->
console.log(error.message) if error.message?
console.log(error.stack) if error.stack?
{app} = require 'electron'
fs = require 'fs-plus'
path = require 'path'
temp = require 'temp'
yargs = require 'yargs'
previousConsoleLog = console.log
startCrashReporter = require('../crash-reporter-start')
console.log = require 'nslog'
start = ->
args = parseCommandLine()
args.env = process.env
setupAtomHome(args)
setupCompileCache()
if handleStartupEventWithSquirrel()
return
else if args.test and args.mainProcess
console.log = previousConsoleLog
testRunner = require(path.join(args.resourcePath, 'spec/main-process/mocha-test-runner'))
app.on 'ready', -> testRunner(args.pathsToOpen)
return
# NB: This prevents Win10 from showing dupe items in the taskbar
app.setAppUserModelId('com.squirrel.atom.atom')
addPathToOpen = (event, pathToOpen) ->
event.preventDefault()
args.pathsToOpen.push(pathToOpen)
addUrlToOpen = (event, urlToOpen) ->
event.preventDefault()
args.urlsToOpen.push(urlToOpen)
app.on 'open-file', addPathToOpen
app.on 'open-url', addUrlToOpen
app.on 'will-finish-launching', startCrashReporter
if args.userDataDir?
app.setPath('userData', args.userDataDir)
else if args.test
app.setPath('userData', temp.mkdirSync('atom-test-data'))
app.on 'ready', ->
app.removeListener 'open-file', addPathToOpen
app.removeListener 'open-url', addUrlToOpen
AtomApplication = require path.join(args.resourcePath, 'src', 'main-process', 'atom-application')
AtomApplication.open(args)
console.log("App load time: #{Date.now() - global.shellStartTime}ms") unless args.test
normalizeDriveLetterName = (filePath) ->
if process.platform is 'win32'
filePath.replace /^([a-z]):/, ([driveLetter]) -> driveLetter.toUpperCase() + ":"
else
filePath
handleStartupEventWithSquirrel = ->
return false unless process.platform is 'win32'
SquirrelUpdate = require './squirrel-update'
squirrelCommand = process.argv[1]
SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
setupAtomHome = ({setPortable}) ->
return if process.env.ATOM_HOME
atomHome = path.join(app.getPath('home'), '.atom')
AtomPortable = require './atom-portable'
if setPortable and not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)
try
AtomPortable.setPortable(atomHome)
catch error
console.log("Failed copying portable directory '#{atomHome}' to '#{AtomPortable.getPortableAtomHomePath()}'")
console.log("#{error.message} #{error.stack}")
if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)
atomHome = AtomPortable.getPortableAtomHomePath()
try
atomHome = fs.realpathSync(atomHome)
process.env.ATOM_HOME = atomHome
setupCompileCache = ->
compileCache = require('../compile-cache')
compileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
writeFullVersion = ->
process.stdout.write """
Atom : #{app.getVersion()}
Electron: #{process.versions.electron}
Chrome : #{process.versions.chrome}
Node : #{process.versions.node}
"""
parseCommandLine = ->
version = app.getVersion()
options = yargs(process.argv[1..]).wrap(100)
options.usage """
Atom Editor v#{version}
Usage: atom [options] [path ...]
One or more paths to files or folders may be specified. If there is an
existing Atom window that contains all of the given folders, the paths
will be opened in that window. Otherwise, they will be opened in a new
window.
Environment Variables:
ATOM_DEV_RESOURCE_PATH The path from which Atom loads source code in dev mode.
Defaults to `~/github/atom`.
ATOM_HOME The root path for all configuration files and folders.
Defaults to `~/.atom`.
"""
# Deprecated 1.0 API preview flag
options.alias('1', 'one').boolean('1').describe('1', 'This option is no longer supported.')
options.boolean('include-deprecated-apis').describe('include-deprecated-apis', 'This option is not currently supported.')
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the main process in the foreground.')
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
options.boolean('profile-startup').describe('profile-startup', 'Create a profile of the startup execution time.')
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the Atom source directory and enable dev-mode.')
options.boolean('safe').describe('safe', 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.')
options.boolean('portable').describe('portable', 'Set portable mode. Copies the ~/.atom folder to be a sibling of the installed Atom location if a .atom folder is not already there.')
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
options.alias('m', 'main-process').boolean('m').describe('m', 'Run the specified specs in the main process.')
options.string('timeout').describe('timeout', 'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).')
options.alias('v', 'version').boolean('v').describe('v', 'Print the version information.')
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
options.alias('a', 'add').boolean('a').describe('add', 'Open path as a new project in last used window.')
options.string('socket-path')
options.string('user-data-dir')
options.boolean('clear-window-state').describe('clear-window-state', 'Delete all Atom environment state.')
args = options.argv
if args.help
process.stdout.write(options.help())
process.exit(0)
if args.version
writeFullVersion()
process.exit(0)
addToLastWindow = args['add']
executedFrom = args['executed-from']?.toString() ? process.cwd()
devMode = args['dev']
safeMode = args['safe']
pathsToOpen = args._
test = args['test']
mainProcess = args['main-process']
timeout = args['timeout']
newWindow = args['new-window']
pidToKillWhenClosed = args['pid'] if args['wait']
logFile = args['log-file']
socketPath = args['socket-path']
userDataDir = args['user-data-dir']
profileStartup = args['profile-startup']
clearWindowState = args['clear-window-state']
urlsToOpen = []
devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getPath('home'), 'github', 'atom')
setPortable = args.portable
if args['resource-path']
devMode = true
resourcePath = args['resource-path']
devMode = true if test
resourcePath ?= devResourcePath if devMode
unless fs.statSyncNoException(resourcePath)
resourcePath = path.dirname(path.dirname(__dirname))
# On Yosemite the $PATH is not inherited by the "open" command, so we have to
# explicitly pass it by command line, see http://git.io/YC8_Ew.
process.env.PATH = args['path-environment'] if args['path-environment']
resourcePath = normalizeDriveLetterName(resourcePath)
devResourcePath = normalizeDriveLetterName(devResourcePath)
{resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
version, pidToKillWhenClosed, devMode, safeMode, newWindow,
logFile, socketPath, userDataDir, profileStartup, timeout, setPortable,
clearWindowState, addToLastWindow, mainProcess}
start()

263
src/main-process/main.js Normal file
View File

@@ -0,0 +1,263 @@
global.shellStartTime = Date.now()
process.on('uncaughtException', function (error = {}) {
if (error.message != null) {
console.log(error.message)
}
if (error.stack != null) {
console.log(error.stack)
}
})
const {app} = require('electron')
const fs = require('fs-plus')
const path = require('path')
const temp = require('temp')
const yargs = require('yargs')
const dedent = require('dedent')
const startCrashReporter = require('../crash-reporter-start')
const previousConsoleLog = console.log
console.log = require('nslog')
function start () {
const args = parseCommandLine()
args.env = process.env
setupAtomHome(args)
setupCompileCache()
if (handleStartupEventWithSquirrel()) {
return
} else if (args.test && args.mainProcess) {
console.log = previousConsoleLog
app.on('ready', function () {
const testRunner = require(path.join(args.resourcePath, 'spec/main-process/mocha-test-runner'))
testRunner(args.pathsToOpen)
})
return
}
// NB: This prevents Win10 from showing dupe items in the taskbar
app.setAppUserModelId('com.squirrel.atom.atom')
function addPathToOpen (event, pathToOpen) {
event.preventDefault()
args.pathsToOpen.push(pathToOpen)
}
function addUrlToOpen (event, urlToOpen) {
event.preventDefault()
args.urlsToOpen.push(urlToOpen)
}
app.on('open-file', addPathToOpen)
app.on('open-url', addUrlToOpen)
app.on('will-finish-launching', startCrashReporter)
if (args.userDataDir != null) {
app.setPath('userData', args.userDataDir)
} else if (args.test) {
app.setPath('userData', temp.mkdirSync('atom-test-data'))
}
app.on('ready', function () {
app.removeListener('open-file', addPathToOpen)
app.removeListener('open-url', addUrlToOpen)
const AtomApplication = require(path.join(args.resourcePath, 'src', 'main-process', 'atom-application'))
AtomApplication.open(args)
if (!args.test) {
console.log(`App load time: ${Date.now() - global.shellStartTime}ms`)
}
})
}
function normalizeDriveLetterName (filePath) {
if (process.platform === 'win32') {
return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':')
} else {
return filePath
}
}
function handleStartupEventWithSquirrel () {
if (process.platform !== 'win32') {
return false
}
const SquirrelUpdate = require('./squirrel-update')
const squirrelCommand = process.argv[1]
return SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
}
function setupAtomHome ({setPortable}) {
if (process.env.ATOM_HOME) {
return
}
let atomHome = path.join(app.getPath('home'), '.atom')
const AtomPortable = require('./atom-portable')
if (setPortable && !AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)) {
try {
AtomPortable.setPortable(atomHome)
} catch (error) {
console.log(`Failed copying portable directory '${atomHome}' to '${AtomPortable.getPortableAtomHomePath()}'`)
console.log(`${error.message} ${error.stack}`)
}
}
if (AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome)) {
atomHome = AtomPortable.getPortableAtomHomePath()
}
try {
atomHome = fs.realpathSync(atomHome)
} finally {
process.env.ATOM_HOME = atomHome
}
}
function setupCompileCache () {
const CompileCache = require('../compile-cache')
CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
}
function writeFullVersion () {
process.stdout.write(
`Atom : ${app.getVersion()}\n` +
`Electron: ${process.versions.electron}\n` +
`Chrome : ${process.versions.chrome}\n` +
`Node : ${process.versions.node}\n`
)
}
function parseCommandLine () {
const options = yargs(process.argv.slice(1)).wrap(100)
const version = app.getVersion()
options.usage(
dedent`Atom Editor v${version}
Usage: atom [options] [path ...]
One or more paths to files or folders may be specified. If there is an
existing Atom window that contains all of the given folders, the paths
will be opened in that window. Otherwise, they will be opened in a new
window.
Environment Variables:
ATOM_DEV_RESOURCE_PATH The path from which Atom loads source code in dev mode.
Defaults to \`~/github/atom\`.
ATOM_HOME The root path for all configuration files and folders.
Defaults to \`~/.atom\`.`
)
// Deprecated 1.0 API preview flag
options.alias('1', 'one').boolean('1').describe('1', 'This option is no longer supported.')
options.boolean('include-deprecated-apis').describe('include-deprecated-apis', 'This option is not currently supported.')
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the main process in the foreground.')
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
options.boolean('profile-startup').describe('profile-startup', 'Create a profile of the startup execution time.')
options.alias('r', 'resource-path').string('r').describe('r', 'Set the path to the Atom source directory and enable dev-mode.')
options.boolean('safe').describe(
'safe',
'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.'
)
options.boolean('portable').describe(
'portable',
'Set portable mode. Copies the ~/.atom folder to be a sibling of the installed Atom location if a .atom folder is not already there.'
)
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
options.alias('m', 'main-process').boolean('m').describe('m', 'Run the specified specs in the main process.')
options.string('timeout').describe(
'timeout',
'When in test mode, waits until the specified time (in minutes) and kills the process (exit code: 130).'
)
options.alias('v', 'version').boolean('v').describe('v', 'Print the version information.')
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
options.alias('a', 'add').boolean('a').describe('add', 'Open path as a new project in last used window.')
options.string('socket-path')
options.string('user-data-dir')
options.boolean('clear-window-state').describe('clear-window-state', 'Delete all Atom environment state.')
const args = options.argv
if (args.help) {
process.stdout.write(options.help())
process.exit(0)
}
if (args.version) {
writeFullVersion()
process.exit(0)
}
const addToLastWindow = args['add']
const safeMode = args['safe']
const pathsToOpen = args._
const test = args['test']
const mainProcess = args['main-process']
const timeout = args['timeout']
const newWindow = args['new-window']
let executedFrom = null
if (args['executed-from'] && args['executed-from'].toString()) {
executedFrom = args['executed-from'].toString()
} else {
executedFrom = process.cwd()
}
let pidToKillWhenClosed = null
if (args['wait']) {
pidToKillWhenClosed = args['pid']
}
const logFile = args['log-file']
const socketPath = args['socket-path']
const userDataDir = args['user-data-dir']
const profileStartup = args['profile-startup']
const clearWindowState = args['clear-window-state']
const urlsToOpen = []
const setPortable = args.portable
let devMode = args['dev']
let devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH || path.join(app.getPath('home'), 'github', 'atom')
let resourcePath = null
if (args['resource-path']) {
devMode = true
resourcePath = args['resource-path']
}
if (test) {
devMode = true
}
if (devMode && !resourcePath) {
resourcePath = devResourcePath
}
if (!fs.statSyncNoException(resourcePath)) {
resourcePath = path.dirname(path.dirname(__dirname))
}
if (args['path-environment']) {
// On Yosemite the $PATH is not inherited by the "open" command, so we have to
// explicitly pass it by command line, see http://git.io/YC8_Ew.
process.env.PATH = args['path-environment']
}
resourcePath = normalizeDriveLetterName(resourcePath)
devResourcePath = normalizeDriveLetterName(devResourcePath)
return {
resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test,
version, pidToKillWhenClosed, devMode, safeMode, newWindow, logFile, socketPath,
userDataDir, profileStartup, timeout, setPortable, clearWindowState,
addToLastWindow, mainProcess
}
}
start()

View File

@@ -1,7 +1,7 @@
fs = require 'fs-plus'
path = require 'path'
Spawner = require './spawner'
WinRegistry = require './win-registry'
WinShell = require './win-shell'
WinPowerShell = require './win-powershell'
appFolder = path.resolve(process.execPath, '..')
@@ -125,26 +125,36 @@ exports.restartAtom = (app) ->
app.once 'will-quit', -> Spawner.spawn(path.join(binFolder, 'atom.cmd'), args)
app.quit()
updateContextMenus = (callback) ->
WinShell.fileContextMenu.update ->
WinShell.folderContextMenu.update ->
WinShell.folderBackgroundContextMenu.update ->
callback()
# Handle squirrel events denoted by --squirrel-* command line arguments.
exports.handleStartupEvent = (app, squirrelCommand) ->
switch squirrelCommand
when '--squirrel-install'
createShortcuts ->
WinRegistry.installContextMenu ->
addCommandsToPath ->
app.quit()
addCommandsToPath ->
WinShell.fileHandler.register ->
updateContextMenus ->
app.quit()
true
when '--squirrel-updated'
updateShortcuts ->
WinRegistry.installContextMenu ->
addCommandsToPath ->
addCommandsToPath ->
updateContextMenus ->
app.quit()
true
when '--squirrel-uninstall'
removeShortcuts ->
WinRegistry.uninstallContextMenu ->
removeCommandsFromPath ->
app.quit()
removeCommandsFromPath ->
WinShell.fileHandler.deregister ->
WinShell.fileContextMenu.deregister ->
WinShell.folderContextMenu.deregister ->
WinShell.folderBackgroundContextMenu.deregister ->
app.quit()
true
when '--squirrel-obsolete'
app.quit()

View File

@@ -1,62 +0,0 @@
path = require 'path'
Spawner = require './spawner'
if process.env.SystemRoot
system32Path = path.join(process.env.SystemRoot, 'System32')
regPath = path.join(system32Path, 'reg.exe')
else
regPath = 'reg.exe'
# Registry keys used for context menu
fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom'
directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom'
backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom'
applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe'
# Spawn reg.exe and callback when it completes
spawnReg = (args, callback) ->
Spawner.spawn(regPath, args, callback)
# Install the Open with Atom explorer context menu items via the registry.
#
# * `callback` The {Function} to call after registry operation is done.
# It will be invoked with the same arguments provided by {Spawner.spawn}.
#
# Returns `undefined`.
exports.installContextMenu = (callback) ->
addToRegistry = (args, callback) ->
args.unshift('add')
args.push('/f')
spawnReg(args, callback)
installFileHandler = (callback) ->
args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""]
addToRegistry(args, callback)
installMenu = (keyPath, arg, callback) ->
args = [keyPath, '/ve', '/d', 'Open with Atom']
addToRegistry args, ->
args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""]
addToRegistry args, ->
args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""]
addToRegistry(args, callback)
installMenu fileKeyPath, '%1', ->
installMenu directoryKeyPath, '%1', ->
installMenu backgroundKeyPath, '%V', ->
installFileHandler(callback)
# Uninstall the Open with Atom explorer context menu items via the registry.
#
# * `callback` The {Function} to call after registry operation is done.
# It will be invoked with the same arguments provided by {Spawner.spawn}.
#
# Returns `undefined`.
exports.uninstallContextMenu = (callback) ->
deleteFromRegistry = (keyPath, callback) ->
spawnReg(['delete', keyPath, '/f'], callback)
deleteFromRegistry fileKeyPath, ->
deleteFromRegistry directoryKeyPath, ->
deleteFromRegistry backgroundKeyPath, ->
deleteFromRegistry(applicationsKeyPath, callback)

View File

@@ -0,0 +1,57 @@
Registry = require 'winreg'
Path = require 'path'
exeName = Path.basename(process.execPath)
appPath = "\"#{process.execPath}\""
appName = exeName.replace('atom', 'Atom').replace('beta', 'Beta').replace('.exe', '')
class ShellOption
constructor: (key, parts) ->
@key = key
@parts = parts
isRegistered: (callback) =>
new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"})
.get @parts[0].name, (err, val) =>
callback(not err? and val.value is @parts[0].value)
register: (callback) =>
doneCount = @parts.length
for part in @parts
reg = new Registry({hive: 'HKCU', key: if part.key? then "#{@key}\\#{part.key}" else @key})
reg.create( -> reg.set part.name, Registry.REG_SZ, part.value, -> callback() if --doneCount is 0)
deregister: (callback) =>
@isRegistered (isRegistered) =>
if isRegistered
new Registry({hive: 'HKCU', key: @key}).destroy -> callback null, true
else
callback null, false
update: (callback) =>
new Registry({hive: 'HKCU', key: "#{@key}\\#{@parts[0].key}"})
.get @parts[0].name, (err, val) =>
if err? or not val.value.includes '\\' + exeName
callback(err)
else
@register callback
exports.appName = appName
exports.fileHandler = new ShellOption("\\Software\\Classes\\Applications\\#{exeName}",
[{key: 'shell\\open\\command', name: '', value: "#{appPath} \"%1\""}]
)
contextParts = [
{key: 'command', name: '', value: "#{appPath} \"%1\""},
{name: '', value: "Open with #{appName}"},
{name: 'Icon', value: "#{appPath}"}
]
exports.fileContextMenu = new ShellOption("\\Software\\Classes\\*\\shell\\#{appName}", contextParts)
exports.folderContextMenu = new ShellOption("\\Software\\Classes\\Directory\\shell\\#{appName}", contextParts)
exports.folderBackgroundContextMenu = new ShellOption("\\Software\\Classes\\Directory\\background\\shell\\#{appName}",
JSON.parse(JSON.stringify(contextParts).replace('%1', '%V'))
)

View File

@@ -159,6 +159,7 @@ class Package
# TODO: Remove. Settings view calls this method currently.
activateConfig: ->
return if @configSchemaRegisteredOnLoad
@requireMainModule()
@registerConfigSchemaFromMainModule()

View File

@@ -344,7 +344,12 @@ class TextEditorComponent
onTextInput: (event) =>
event.stopPropagation()
event.preventDefault()
# WARNING: If we call preventDefault on the input of a space character,
# then the browser interprets the spacebar keypress as a page-down command,
# causing spaces to scroll elements containing editors. This is impossible
# to test.
event.preventDefault() if event.data isnt ' '
return unless @isInputEnabled()