mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Merge remote-tracking branch 'origin/master' into weekly-2018-03-12
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# Near-term plans
|
||||
|
||||
In this directory, you'll find weekly progress and plans from the core Atom team at GitHub. In addition, this document summarizes the work we're intending to prioritize within the next several months.
|
||||
Want to know what the Atom team is working on and what has our focus over the next few months? You've come to the right place. 🎯
|
||||
|
||||
In this directory, you'll find **weekly progress and planning updates** from the core Atom team at GitHub (e.g., [`2018-02-12.md`](2018-02-12.md)), and the sections below represent our **near-term roadmap**:
|
||||
|
||||
* [Atom IDE](#atom-ide)
|
||||
* [GitHub package](#github-package)
|
||||
@@ -8,19 +10,25 @@ In this directory, you'll find weekly progress and plans from the core Atom team
|
||||
* [Tree-sitter](#tree-sitter)
|
||||
* [Xray](#xray)
|
||||
|
||||
This roadmap is a [living document](https://en.wikipedia.org/wiki/Living_document): it represents our current plans, but we expect these plans to change from time to time.
|
||||
|
||||
---
|
||||
|
||||
# Atom IDE
|
||||
|
||||
## Roadmap
|
||||
|
||||
## Looking ahead
|
||||
TODO
|
||||
|
||||
## Looking farther ahead
|
||||
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
# GitHub package
|
||||
|
||||
- [atom/github](http://github.com/atom/github) (Atom package)
|
||||
Main repository: [atom/github](http://github.com/atom/github) (Atom package)
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -66,7 +74,7 @@ _Longer-term goals:_ Finish the credential handler refactor begun in [#846](http
|
||||
|
||||
* Improve our handling of 2FA credentials. Ideally we could detect when a user has 2FA enabled and prompt for a one-time code. [#844](https://github.com/atom/github/issues/844)
|
||||
|
||||
## Looking ahead
|
||||
## Looking farther ahead
|
||||
|
||||
In no particular order:
|
||||
|
||||
@@ -82,9 +90,45 @@ In no particular order:
|
||||
|
||||
# Teletype
|
||||
|
||||
Main repository: [atom/teletype](http://github.com/atom/teletype) (Atom package)
|
||||
|
||||
## Roadmap
|
||||
|
||||
## Looking ahead
|
||||
##### 1. Deliver a multi-file collaboration experience that meets 80% of the needs with 20% of the effort
|
||||
|
||||
- Ship RFC-001 (https://github.com/atom/teletype/issues/268)
|
||||
|
||||
##### 2. Streamline collaboration set-up
|
||||
|
||||
Near-term goal: Encourage more collaboration by reducing barriers to entry.
|
||||
|
||||
Longer-term goal: Provide the world's fastest transition from "I want to collaborate" to "I am collaborating." 🚀
|
||||
|
||||
- Publish RFC (including a request for review from GitHub's Community and Safety team)
|
||||
- Host can share a URL for the portal, and guests can follow the URL to instantly join the portal (https://github.com/atom/teletype/issues/109)
|
||||
- Quickly collaborate with coworkers and friends (https://github.com/atom/teletype/issues/213, https://github.com/atom/teletype/issues/284)
|
||||
- You can view a list of past collaborators (i.e., a ["buddy list"](https://github.com/atom/teletype/issues/22) of sorts).
|
||||
- You can choose any online person in the buddy list and invite them to join your portal. They get a notification (or similar) informing them of the invitation, and they can choose to join the portal or not.
|
||||
- To prevent abuse/harassment, each time you join a portal via a URL or portal ID, Teletype adds the collaborators to your buddy list. You can directly invite anyone in your buddy list to join your portal, and anyone in your buddy list can invite you to a portal. You can remove anyone from your buddy list, at which point they can no longer _directly_ invite you to a portal.
|
||||
|
||||
##### 3. Nice bang-for-the-buck refinements
|
||||
|
||||
- Add a colored border around avatars that matches the cursor when that participant's tether is not retracted (https://github.com/atom/teletype/issues/338)
|
||||
|
||||
##### 4. Prioritized bugs
|
||||
|
||||
- Uncaught TypeError: Cannot match against 'undefined' or 'null' (https://github.com/atom/teletype/issues/233)
|
||||
|
||||
## Looking farther ahead
|
||||
|
||||
In no particular order:
|
||||
|
||||
- 🐛 Resolve or reduce impact of package initialization errors (https://github.com/atom/teletype/issues/266)
|
||||
- 🐛 Surface uncaught errors in promises (https://github.com/atom/teletype/issues/298#issuecomment-355369327)
|
||||
- ✨ Ensure remote buffers are updated when host renames files (https://github.com/atom/teletype/issues/147)
|
||||
- 💖 In the buddy list, you can see which people are currently online (i.e., presence)
|
||||
- 💖 Screen-sharing -- (We should prioritize screen-sharing above audio. We can keep using Slack/Skype/Zoom/Whatever for audio and use Atom for screen-sharing, whereas the opposite is not true; disabling audio on a Slack call would feel unintuitive.)
|
||||
- 💖 Audio
|
||||
|
||||
---
|
||||
|
||||
@@ -92,7 +136,11 @@ In no particular order:
|
||||
|
||||
## Roadmap
|
||||
|
||||
## Looking ahead
|
||||
TODO
|
||||
|
||||
## Looking farther ahead
|
||||
|
||||
TODO
|
||||
|
||||
---
|
||||
|
||||
@@ -100,4 +148,8 @@ In no particular order:
|
||||
|
||||
## Roadmap
|
||||
|
||||
## Looking ahead
|
||||
TODO
|
||||
|
||||
## Looking farther ahead
|
||||
|
||||
TODO
|
||||
|
||||
10
package.json
10
package.json
@@ -148,15 +148,15 @@
|
||||
"language-go": "0.45.2",
|
||||
"language-html": "0.49.0",
|
||||
"language-hyperlink": "0.16.3",
|
||||
"language-java": "0.28.0",
|
||||
"language-javascript": "0.128.3",
|
||||
"language-java": "0.29.0",
|
||||
"language-javascript": "0.128.4",
|
||||
"language-json": "0.19.1",
|
||||
"language-less": "0.34.2",
|
||||
"language-make": "0.22.3",
|
||||
"language-mustache": "0.14.5",
|
||||
"language-objective-c": "0.15.1",
|
||||
"language-perl": "0.38.1",
|
||||
"language-php": "0.43.1",
|
||||
"language-php": "0.43.2",
|
||||
"language-property-list": "0.9.1",
|
||||
"language-python": "0.49.2",
|
||||
"language-ruby": "0.71.4",
|
||||
@@ -165,12 +165,12 @@
|
||||
"language-shellscript": "0.26.2",
|
||||
"language-source": "0.9.0",
|
||||
"language-sql": "0.25.10",
|
||||
"language-text": "0.7.3",
|
||||
"language-text": "0.7.4",
|
||||
"language-todo": "0.29.4",
|
||||
"language-toml": "0.18.2",
|
||||
"language-typescript": "0.3.2",
|
||||
"language-xml": "0.35.2",
|
||||
"language-yaml": "0.31.2"
|
||||
"language-yaml": "0.32.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
const path = require('path')
|
||||
const temp = require('temp').track()
|
||||
const fs = require('fs-plus')
|
||||
|
||||
describe('Config', () => {
|
||||
let savedSettings
|
||||
|
||||
@@ -490,7 +486,6 @@ describe('Config', () => {
|
||||
atom.config.set('foo.bar.baz', 'value 2')
|
||||
expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 2', oldValue: 'value 1'})
|
||||
observeHandler.reset()
|
||||
|
||||
observeHandler.andCallFake(() => { throw new Error('oops') })
|
||||
expect(() => atom.config.set('foo.bar.baz', 'value 1')).toThrow('oops')
|
||||
expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 1', oldValue: 'value 2'})
|
||||
@@ -1840,4 +1835,86 @@ describe('Config', () => {
|
||||
expect(atom.config.get('do.ray')).toBe('me')
|
||||
})
|
||||
})
|
||||
|
||||
describe('project specific settings', () => {
|
||||
describe('config.resetProjectSettings', () => {
|
||||
it('gracefully handles invalid config objects', () => {
|
||||
atom.config.resetProjectSettings({})
|
||||
expect(atom.config.get('foo.bar')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('config.get', () => {
|
||||
const dummyPath = '/Users/dummy/path.json'
|
||||
describe('project settings', () => {
|
||||
it('returns a deep clone of the property value', () => {
|
||||
atom.config.resetProjectSettings({'*': {'value': {array: [1, {b: 2}, 3]}}}, dummyPath)
|
||||
const retrievedValue = atom.config.get('value')
|
||||
retrievedValue.array[0] = 4
|
||||
retrievedValue.array[1].b = 2.1
|
||||
expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]})
|
||||
})
|
||||
|
||||
it('properly gets project settings', () => {
|
||||
atom.config.resetProjectSettings({'*': {'foo': 'wei'}}, dummyPath)
|
||||
expect(atom.config.get('foo')).toBe('wei')
|
||||
atom.config.resetProjectSettings({'*': {'foo': {'bar': 'baz'}}}, dummyPath)
|
||||
expect(atom.config.get('foo.bar')).toBe('baz')
|
||||
})
|
||||
|
||||
it('gets project settings with higher priority than regular settings', () => {
|
||||
atom.config.set('foo', 'bar')
|
||||
atom.config.resetProjectSettings({'*': {'foo': 'baz'}}, dummyPath)
|
||||
expect(atom.config.get('foo')).toBe('baz')
|
||||
})
|
||||
|
||||
it('correctly gets nested and scoped properties for project settings', () => {
|
||||
expect(atom.config.set('foo.bar.str', 'global')).toBe(true)
|
||||
expect(atom.config.set('foo.bar.str', 'scoped', {scopeSelector: '.source.js'})).toBe(true)
|
||||
expect(atom.config.get('foo.bar.str')).toBe('global')
|
||||
expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped')
|
||||
})
|
||||
|
||||
it('returns a deep clone of the property value', () => {
|
||||
atom.config.set('value', {array: [1, {b: 2}, 3]})
|
||||
const retrievedValue = atom.config.get('value')
|
||||
retrievedValue.array[0] = 4
|
||||
retrievedValue.array[1].b = 2.1
|
||||
expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]})
|
||||
})
|
||||
|
||||
it('gets scoped values correctly', () => {
|
||||
atom.config.set('foo', 'bam', {scope: ['second']})
|
||||
expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam')
|
||||
atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath)
|
||||
expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('baz')
|
||||
atom.config.clearProjectSettings()
|
||||
expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam')
|
||||
})
|
||||
|
||||
it('clears project settings correctly', () => {
|
||||
atom.config.set('foo', 'bar')
|
||||
expect(atom.config.get('foo')).toBe('bar')
|
||||
atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath)
|
||||
expect(atom.config.get('foo')).toBe('baz')
|
||||
expect(atom.config.getSources().length).toBe(1)
|
||||
atom.config.clearProjectSettings()
|
||||
expect(atom.config.get('foo')).toBe('bar')
|
||||
expect(atom.config.getSources().length).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('config.getAll', () => {
|
||||
const dummyPath = '/Users/dummy/path.json'
|
||||
it('gets settings in the same way .get would return them', () => {
|
||||
atom.config.resetProjectSettings({'*': {'a': 'b'}}, dummyPath)
|
||||
atom.config.set('a', 'f')
|
||||
expect(atom.config.getAll('a')).toEqual([{
|
||||
scopeSelector: '*',
|
||||
value: 'b'
|
||||
}])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -274,6 +274,51 @@ describe('Project', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.replace', () => {
|
||||
let projectSpecification, projectPath1, projectPath2
|
||||
beforeEach(() => {
|
||||
atom.project.replace(null)
|
||||
projectPath1 = temp.mkdirSync('project-path1')
|
||||
projectPath2 = temp.mkdirSync('project-path2')
|
||||
projectSpecification = {
|
||||
paths: [projectPath1, projectPath2],
|
||||
originPath: 'originPath',
|
||||
config: {
|
||||
'baz': 'buzz'
|
||||
}
|
||||
}
|
||||
})
|
||||
it('sets a project specification', () => {
|
||||
expect(atom.config.get('baz')).toBeUndefined()
|
||||
atom.project.replace(projectSpecification)
|
||||
expect(atom.project.getPaths()).toEqual([projectPath1, projectPath2])
|
||||
expect(atom.config.get('baz')).toBe('buzz')
|
||||
})
|
||||
|
||||
it('clears a project through replace with no params', () => {
|
||||
expect(atom.config.get('baz')).toBeUndefined()
|
||||
atom.project.replace(projectSpecification)
|
||||
expect(atom.config.get('baz')).toBe('buzz')
|
||||
expect(atom.project.getPaths()).toEqual([projectPath1, projectPath2])
|
||||
atom.project.replace()
|
||||
expect(atom.config.get('baz')).toBeUndefined()
|
||||
expect(atom.project.getPaths()).toEqual([])
|
||||
})
|
||||
|
||||
it('responds to change of project specification', () => {
|
||||
let wasCalled = false
|
||||
const callback = () => {
|
||||
wasCalled = true
|
||||
}
|
||||
atom.project.onDidReplace(callback)
|
||||
atom.project.replace(projectSpecification)
|
||||
expect(wasCalled).toBe(true)
|
||||
wasCalled = false
|
||||
atom.project.replace()
|
||||
expect(wasCalled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('before and after saving a buffer', () => {
|
||||
let buffer
|
||||
beforeEach(() =>
|
||||
|
||||
@@ -50,7 +50,6 @@ let nextId = 0
|
||||
//
|
||||
// An instance of this class is always available as the `atom` global.
|
||||
class AtomEnvironment {
|
||||
|
||||
/*
|
||||
Section: Properties
|
||||
*/
|
||||
@@ -210,7 +209,7 @@ class AtomEnvironment {
|
||||
this.blobStore = params.blobStore
|
||||
this.configDirPath = params.configDirPath
|
||||
|
||||
const {devMode, safeMode, resourcePath, userSettings} = this.getLoadSettings()
|
||||
const {devMode, safeMode, resourcePath, userSettings, projectSpecification} = this.getLoadSettings()
|
||||
|
||||
ConfigSchema.projectHome = {
|
||||
type: 'string',
|
||||
@@ -224,6 +223,10 @@ class AtomEnvironment {
|
||||
})
|
||||
this.config.resetUserSettings(userSettings)
|
||||
|
||||
if (projectSpecification != null && projectSpecification.config != null) {
|
||||
this.project.replace(projectSpecification)
|
||||
}
|
||||
|
||||
this.menu.initialize({resourcePath})
|
||||
this.contextMenu.initialize({resourcePath, devMode})
|
||||
|
||||
@@ -788,6 +791,7 @@ class AtomEnvironment {
|
||||
this.disposables.add(this.applicationDelegate.onDidFailToReadUserSettings(message =>
|
||||
this.notifications.addError(message)
|
||||
))
|
||||
|
||||
this.disposables.add(this.applicationDelegate.onDidOpenLocations(this.openLocations.bind(this)))
|
||||
this.disposables.add(this.applicationDelegate.onApplicationMenuCommand(this.dispatchApplicationMenuCommand.bind(this)))
|
||||
this.disposables.add(this.applicationDelegate.onContextMenuCommand(this.dispatchContextMenuCommand.bind(this)))
|
||||
|
||||
@@ -422,8 +422,12 @@ class Config {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
|
||||
this.defaultSettings = {}
|
||||
this.settings = {}
|
||||
this.projectSettings = {}
|
||||
this.projectFile = null
|
||||
|
||||
this.scopedSettingsStore = new ScopedPropertyStore()
|
||||
|
||||
this.settingsLoaded = false
|
||||
@@ -621,10 +625,10 @@ class Config {
|
||||
legacyScopeDescriptor = this.getLegacyScopeDescriptorForNewScopeDescriptor(scopeDescriptor)
|
||||
if (legacyScopeDescriptor) {
|
||||
result.push(...Array.from(this.scopedSettingsStore.getAll(
|
||||
legacyScopeDescriptor.getScopeChain(),
|
||||
keyPath,
|
||||
options
|
||||
) || []))
|
||||
legacyScopeDescriptor.getScopeChain(),
|
||||
keyPath,
|
||||
options
|
||||
) || []))
|
||||
}
|
||||
} else {
|
||||
result = []
|
||||
@@ -691,7 +695,7 @@ class Config {
|
||||
let source = options.source
|
||||
const shouldSave = options.save != null ? options.save : true
|
||||
|
||||
if (source && !scopeSelector) {
|
||||
if (source && !scopeSelector && source !== this.projectFile) {
|
||||
throw new Error("::set with a 'source' and no 'sourceSelector' is not yet implemented!")
|
||||
}
|
||||
|
||||
@@ -708,7 +712,7 @@ class Config {
|
||||
if (scopeSelector != null) {
|
||||
this.setRawScopedValue(keyPath, value, source, scopeSelector)
|
||||
} else {
|
||||
this.setRawValue(keyPath, value)
|
||||
this.setRawValue(keyPath, value, {source})
|
||||
}
|
||||
|
||||
if (source === this.mainSource && shouldSave && this.settingsLoaded) {
|
||||
@@ -943,7 +947,12 @@ class Config {
|
||||
Section: Private methods managing global settings
|
||||
*/
|
||||
|
||||
resetUserSettings (newSettings) {
|
||||
resetUserSettings (newSettings, options = {}) {
|
||||
this._resetSettings(newSettings, options)
|
||||
}
|
||||
|
||||
_resetSettings (newSettings, options = {}) {
|
||||
const source = options.source
|
||||
newSettings = Object.assign({}, newSettings)
|
||||
if (newSettings.global != null) {
|
||||
newSettings['*'] = newSettings.global
|
||||
@@ -954,13 +963,16 @@ class Config {
|
||||
const scopedSettings = newSettings
|
||||
newSettings = newSettings['*']
|
||||
delete scopedSettings['*']
|
||||
this.resetUserScopedSettings(scopedSettings)
|
||||
this.resetScopedSettings(scopedSettings, {source})
|
||||
}
|
||||
|
||||
return this.transact(() => {
|
||||
this.settings = {}
|
||||
this._clearUnscopedSettingsForSource(source)
|
||||
this.settingsLoaded = true
|
||||
for (let key in newSettings) { const value = newSettings[key]; this.set(key, value, {save: false}) }
|
||||
for (let key in newSettings) {
|
||||
const value = newSettings[key]
|
||||
this.set(key, value, {save: false, source})
|
||||
}
|
||||
if (this.pendingOperations.length) {
|
||||
for (let op of this.pendingOperations) { op() }
|
||||
this.pendingOperations = []
|
||||
@@ -968,10 +980,39 @@ class Config {
|
||||
})
|
||||
}
|
||||
|
||||
_clearUnscopedSettingsForSource (source) {
|
||||
if (source === this.projectFile) {
|
||||
this.projectSettings = {}
|
||||
} else {
|
||||
this.settings = {}
|
||||
}
|
||||
}
|
||||
|
||||
resetProjectSettings (newSettings, projectFile) {
|
||||
// Sets the scope and source of all project settings to `path`.
|
||||
newSettings = Object.assign({}, newSettings)
|
||||
const oldProjectFile = this.projectFile
|
||||
this.projectFile = projectFile
|
||||
if (this.projectFile != null) {
|
||||
this._resetSettings(newSettings, {source: this.projectFile})
|
||||
} else {
|
||||
this.scopedSettingsStore.removePropertiesForSource(oldProjectFile)
|
||||
this.projectSettings = {}
|
||||
}
|
||||
}
|
||||
|
||||
clearProjectSettings () {
|
||||
this.resetProjectSettings({}, null)
|
||||
}
|
||||
|
||||
getRawValue (keyPath, options = {}) {
|
||||
let value
|
||||
if (!options.excludeSources || !options.excludeSources.includes(this.mainSource)) {
|
||||
value = getValueAtKeyPath(this.settings, keyPath)
|
||||
if (this.projectFile != null) {
|
||||
const projectValue = getValueAtKeyPath(this.projectSettings, keyPath)
|
||||
value = (projectValue === undefined) ? value : projectValue
|
||||
}
|
||||
}
|
||||
|
||||
let defaultValue
|
||||
@@ -990,19 +1031,22 @@ class Config {
|
||||
}
|
||||
}
|
||||
|
||||
setRawValue (keyPath, value) {
|
||||
setRawValue (keyPath, value, options = {}) {
|
||||
const source = options.source ? options.source : undefined
|
||||
const settingsToChange = source === this.projectFile ? 'projectSettings' : 'settings'
|
||||
const defaultValue = getValueAtKeyPath(this.defaultSettings, keyPath)
|
||||
|
||||
if (_.isEqual(defaultValue, value)) {
|
||||
if (keyPath != null) {
|
||||
deleteValueAtKeyPath(this.settings, keyPath)
|
||||
deleteValueAtKeyPath(this[settingsToChange], keyPath)
|
||||
} else {
|
||||
this.settings = null
|
||||
this[settingsToChange] = null
|
||||
}
|
||||
} else {
|
||||
if (keyPath != null) {
|
||||
setValueAtKeyPath(this.settings, keyPath, value)
|
||||
setValueAtKeyPath(this[settingsToChange], keyPath, value)
|
||||
} else {
|
||||
this.settings = value
|
||||
this[settingsToChange] = value
|
||||
}
|
||||
}
|
||||
return this.emitChangeEvent()
|
||||
@@ -1168,15 +1212,22 @@ class Config {
|
||||
*/
|
||||
|
||||
priorityForSource (source) {
|
||||
return (source === this.mainSource) ? 1000 : 0
|
||||
switch (source) {
|
||||
case this.mainSource:
|
||||
return 1000
|
||||
case this.projectFile:
|
||||
return 2000
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
emitChangeEvent () {
|
||||
if (this.transactDepth <= 0) { return this.emitter.emit('did-change') }
|
||||
}
|
||||
|
||||
resetUserScopedSettings (newScopedSettings) {
|
||||
const source = this.mainSource
|
||||
resetScopedSettings (newScopedSettings, options = {}) {
|
||||
const source = options.source == null ? this.mainSource : options.source
|
||||
const priority = this.priorityForSource(source)
|
||||
this.scopedSettingsStore.removePropertiesForSource(source)
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ class AtomApplication extends EventEmitter {
|
||||
this.quitting = false
|
||||
this.getAllWindows = this.getAllWindows.bind(this)
|
||||
this.getLastFocusedWindow = this.getLastFocusedWindow.bind(this)
|
||||
|
||||
this.resourcePath = options.resourcePath
|
||||
this.devResourcePath = options.devResourcePath
|
||||
this.version = options.version
|
||||
@@ -203,6 +202,7 @@ class AtomApplication extends EventEmitter {
|
||||
|
||||
openWithOptions (options) {
|
||||
const {
|
||||
projectSpecification,
|
||||
initialPaths,
|
||||
pathsToOpen,
|
||||
executedFrom,
|
||||
@@ -257,6 +257,7 @@ class AtomApplication extends EventEmitter {
|
||||
profileStartup,
|
||||
clearWindowState,
|
||||
addToLastWindow,
|
||||
projectSpecification,
|
||||
env
|
||||
})
|
||||
} else if (urlsToOpen.length > 0) {
|
||||
@@ -820,6 +821,7 @@ class AtomApplication extends EventEmitter {
|
||||
window,
|
||||
clearWindowState,
|
||||
addToLastWindow,
|
||||
projectSpecification,
|
||||
env
|
||||
} = {}) {
|
||||
if (!pathsToOpen || pathsToOpen.length === 0) return
|
||||
@@ -853,7 +855,7 @@ class AtomApplication extends EventEmitter {
|
||||
}
|
||||
|
||||
let openedWindow
|
||||
if (existingWindow) {
|
||||
if (existingWindow && (projectSpecification == null || projectSpecification.config == null)) {
|
||||
openedWindow = existingWindow
|
||||
openedWindow.openLocations(locationsToOpen)
|
||||
if (openedWindow.isMinimized()) {
|
||||
@@ -878,6 +880,7 @@ class AtomApplication extends EventEmitter {
|
||||
}
|
||||
if (!resourcePath) resourcePath = this.resourcePath
|
||||
if (!windowDimensions) windowDimensions = this.getDimensionsForNewWindow()
|
||||
|
||||
openedWindow = new AtomWindow(this, this.fileRecoveryService, {
|
||||
initialPaths,
|
||||
locationsToOpen,
|
||||
@@ -888,6 +891,7 @@ class AtomApplication extends EventEmitter {
|
||||
windowDimensions,
|
||||
profileStartup,
|
||||
clearWindowState,
|
||||
projectSpecification,
|
||||
env
|
||||
})
|
||||
this.addWindow(openedWindow)
|
||||
|
||||
@@ -22,6 +22,7 @@ class AtomWindow extends EventEmitter {
|
||||
this.safeMode = settings.safeMode
|
||||
this.devMode = settings.devMode
|
||||
this.resourcePath = settings.resourcePath
|
||||
this.projectSpecification = settings.projectSpecification
|
||||
|
||||
let {pathToOpen, locationsToOpen} = settings
|
||||
if (!locationsToOpen && pathToOpen) locationsToOpen = [{pathToOpen}]
|
||||
@@ -57,9 +58,11 @@ class AtomWindow extends EventEmitter {
|
||||
|
||||
Object.defineProperty(this.browserWindow, 'loadSettingsJSON', {
|
||||
get: () => JSON.stringify(Object.assign({
|
||||
userSettings: this.atomApplication.configFile.get()
|
||||
}, this.loadSettings)),
|
||||
configurable: true
|
||||
userSettings: !this.isSpec
|
||||
? this.atomApplication.configFile.get()
|
||||
: null,
|
||||
projectSpecification: this.projectSpecification
|
||||
}, this.loadSettings))
|
||||
})
|
||||
|
||||
this.handleEvents()
|
||||
|
||||
@@ -5,6 +5,7 @@ const yargs = require('yargs')
|
||||
const {app} = require('electron')
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
const CSON = require('season')
|
||||
|
||||
module.exports = function parseCommandLine (processArgs) {
|
||||
const options = yargs(processArgs).wrap(yargs.terminalWidth())
|
||||
@@ -52,6 +53,7 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
'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('p', 'project').describe('p', 'Start Atom with a project specification file.')
|
||||
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')
|
||||
@@ -91,6 +93,7 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
const benchmark = args['benchmark']
|
||||
const benchmarkTest = args['benchmark-test']
|
||||
const test = args['test']
|
||||
const projectSpecificationFile = args['project']
|
||||
const mainProcess = args['main-process']
|
||||
const timeout = args['timeout']
|
||||
const newWindow = args['new-window']
|
||||
@@ -125,6 +128,7 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if project flag is set, then add all paths from the .atomproject.
|
||||
if (args['resource-path']) {
|
||||
devMode = true
|
||||
devResourcePath = args['resource-path']
|
||||
@@ -134,6 +138,28 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
devMode = true
|
||||
}
|
||||
|
||||
let projectSpecification = {}
|
||||
if (projectSpecificationFile) {
|
||||
const readPath = path.isAbsolute(projectSpecificationFile)
|
||||
? projectSpecificationFile
|
||||
: path.join(executedFrom, projectSpecificationFile)
|
||||
|
||||
const contents = Object.assign({}, readProjectSpecificationSync(readPath, executedFrom))
|
||||
const pathToProjectFile = path.join(executedFrom, projectSpecificationFile)
|
||||
|
||||
const base = path.dirname(pathToProjectFile)
|
||||
pathsToOpen.push(path.dirname(projectSpecificationFile))
|
||||
const paths = (contents.paths == null)
|
||||
? undefined
|
||||
: contents.paths.map(curPath => path.resolve(base, curPath))
|
||||
|
||||
projectSpecification = {
|
||||
originPath: pathToProjectFile,
|
||||
paths,
|
||||
config: contents.config
|
||||
}
|
||||
}
|
||||
|
||||
if (devMode) {
|
||||
resourcePath = devResourcePath
|
||||
}
|
||||
@@ -152,6 +178,7 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
devResourcePath = normalizeDriveLetterName(devResourcePath)
|
||||
|
||||
return {
|
||||
projectSpecification,
|
||||
resourcePath,
|
||||
devResourcePath,
|
||||
pathsToOpen,
|
||||
@@ -177,6 +204,18 @@ module.exports = function parseCommandLine (processArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
function readProjectSpecificationSync (filepath, executedFrom) {
|
||||
let contents
|
||||
try {
|
||||
contents = CSON.readFileSync(filepath)
|
||||
} catch (e) {
|
||||
throw new Error('Unable to read supplied project specification file.')
|
||||
}
|
||||
|
||||
contents.config = (contents.config == null) ? {} : contents.config
|
||||
return contents
|
||||
}
|
||||
|
||||
function normalizeDriveLetterName (filePath) {
|
||||
if (process.platform === 'win32') {
|
||||
return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':')
|
||||
|
||||
@@ -77,6 +77,31 @@ class Project extends Model {
|
||||
}
|
||||
}
|
||||
|
||||
// Layers the contents of a project's file's config
|
||||
// on top of the current global config.
|
||||
replace (projectSpecification) {
|
||||
if (projectSpecification == null) {
|
||||
atom.config.clearProjectSettings()
|
||||
this.setPaths([])
|
||||
} else {
|
||||
if (projectSpecification.originPath == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// If no path is specified, set to directory of originPath.
|
||||
if (!Array.isArray(projectSpecification.paths)) {
|
||||
projectSpecification.paths = [path.dirname(projectSpecification.originPath)]
|
||||
}
|
||||
atom.config.resetProjectSettings(projectSpecification.config, projectSpecification.originPath)
|
||||
this.setPaths(projectSpecification.paths)
|
||||
}
|
||||
this.emitter.emit('did-replace', projectSpecification)
|
||||
}
|
||||
|
||||
onDidReplace (callback) {
|
||||
return this.emitter.on('did-replace', callback)
|
||||
}
|
||||
|
||||
/*
|
||||
Section: Serialization
|
||||
*/
|
||||
@@ -323,7 +348,6 @@ class Project extends Model {
|
||||
// a file or does not exist, its parent directory will be added instead.
|
||||
addPath (projectPath, options = {}) {
|
||||
const directory = this.getDirectoryForProjectPath(projectPath)
|
||||
|
||||
let ok = true
|
||||
if (options.exact === true) {
|
||||
ok = (directory.getPath() === projectPath)
|
||||
@@ -353,6 +377,7 @@ class Project extends Model {
|
||||
this.emitter.emit('did-change-files', events)
|
||||
}
|
||||
}
|
||||
|
||||
// We'll use the directory's custom onDidChangeFiles callback, if available.
|
||||
// CustomDirectory::onDidChangeFiles should match the signature of
|
||||
// Project::onDidChangeFiles below (although it may resolve asynchronously)
|
||||
|
||||
Reference in New Issue
Block a user