8000 Add the concept of an `atomproject` file to atom. by philipfweiss · Pull Request #16845 · atom/atom · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
This repository was archived by the owner on Mar 3, 2023. It is now read-only.

Add the concept of an atomproject file to atom. #16845

Merged
merged 45 commits into from
Mar 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bf6a4e3
add .atomProject files
Feb 18, 2018
e990635
remove undefined dir
Feb 18, 2018
e0db977
prevent collision in project name with hash
Feb 19, 2018
c69b8d3
remove whitespace
Feb 19, 2018
ad40ff9
add failure mode for reading project settings
Feb 19, 2018
e2c4808
atom project open in their own windows and behave more sanely
Feb 24, 2018
9eee256
change atomproject key
Feb 25, 2018
1483c0c
add more data passed through with projectSettings
Feb 25, 2018
347d547
guard against edge cases
Feb 25, 2018
a419d47
fix linting issues
Feb 25, 2018
6eee51b
atomProject -> atomproject
Feb 25, 2018
20203b7
update comments for getAll
Feb 25, 2018
b1490e9
fix linting issues
Feb 25, 2018
89fbf1e
change API to clearAtomProject
Feb 25, 2018
f2eede8
add onDidReplaceAtomProject api
Feb 26, 2018
bd4231b
relativize to atom project
Feb 26, 2018
e376096
use setpaths instead of command line paths
Feb 26, 2018
a9fd84d
flesh out clearatomproject
Feb 26, 2018
607b3ff
add callback to ondidreplaceatomproject
Feb 26, 2018
547b067
add callback to onDidReplaceAtomProject and add tests
Feb 26, 2018
48d3677
update project api names
Feb 26, 2018
8567b8f
fix linting issues
Feb 26, 2018
5d6e531
replace -> did-replace
Feb 27, 2018
8f672b2
Merge branch 'master' into fb-pw-simple-project-config
philipfweiss Feb 27, 2018
9fb756f
save entire settings object, rather than just the filename
Mar 1, 2018
9d95fd6
Merge branch 'fb-pw-simple-project-config' of github.com:atom/atom in…
Mar 1, 2018
d5aaf91
update with max's suggestions
Mar 2, 2018
3c02ab3
add more spec
Mar 2, 2018
7ce5b00
address more of max's comments
Mar 2, 2018
232f821
remove getProjectSettings
Mar 2, 2018
fd970c3
remove getallforpriority
Mar 2, 2018
04081ba
fix linting issues
Mar 2, 2018
faf0d78
remove console log
Mar 2, 2018
f111eaa
remove console log
Mar 2, 2018
062dfde
address PR comments
Mar 6, 2018
685292b
fix linting issues
Mar 6, 2018
6024a5f
Fix linting error in config.js
philipfweiss Mar 6, 2018
aa925c2
remove atomproject format restriction
Mar 6, 2018
9b6b842
Merge branch 'fb-pw-simple-project-config' of github.com:atom/atom in…
Mar 6, 2018
b4f9fab
fix nits
Mar 6, 2018
9d3c91b
fix more nits
Mar 9, 2018
a31795e
fix linting issues
Mar 9, 2018
9282f07
return correct contents when no config present
Mar 9, 2018
e14ffb9
set default path inside of project.replace
Mar 9, 2018
d7eff0e
Merge branch 'master' into fb-pw-simple-project-config
philipfweiss Mar 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 82 additions & 5 deletions spec/config-spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
const path = require('path')
const temp = require('temp').track()
const fs = require('fs-plus')

describe('Config', () => {
let savedSettings

Expand Down Expand Up @@ -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'})
Expand Down Expand Up @@ -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'
}])
})
})
})
})
45 changes: 45 additions & 0 deletions spec/project-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() =>
Expand Down
8 changes: 6 additions & 2 deletions src/atom-environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ let nextId = 0
//
// An instance of this class is always available as the `atom` global.
class AtomEnvironment {

/*
Section: Properties
*/
Expand Down Expand Up @@ -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',
Expand All @@ -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})

Expand Down Expand Up @@ -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)))
Expand Down
87 changes: 69 additions & 18 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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!")
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -954,24 +963,56 @@ 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 = []
}
})
}

_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
Expand All @@ -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()
Expand Down Expand Up @@ -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)

Expand Down
Loading
0