diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 7c687ac92..b48c7a773 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -69,6 +69,8 @@ module.exports = (grunt) -> expand: true src: [ 'src/**/*.coffee' + 'spec/*.coffee' + '!spec/*-spec.coffee' 'exports/**/*.coffee' 'static/**/*.coffee' ] @@ -125,10 +127,19 @@ module.exports = (grunt) -> {engines, theme} = grunt.file.readJSON(metadataPath) if engines?.atom? coffeeConfig.glob_to_multiple.src.push("#{directory}/**/*.coffee") + coffeeConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.coffee") + lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less") - prebuildLessConfig.src.push("#{directory}/**/*.less") unless theme + lessConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.less") + + unless theme + prebuildLessConfig.src.push("#{directory}/**/*.less") + prebuildLessConfig.src.push("!#{directory}/spec/**/*.less") + csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson") - pegConfig.glob_to_multiple.src.push("#{directory}/**/*.pegjs") + csonConfig.glob_to_multiple.src.push("!#{directory}/spec/**/*.cson") + + pegConfig.glob_to_multiple.src.push("#{directory}/lib/*.pegjs") grunt.initConfig pkg: grunt.file.readJSON('package.json') diff --git a/build/package.json b/build/package.json index 3f0a3caef..e4612eb6f 100644 --- a/build/package.json +++ b/build/package.json @@ -20,7 +20,7 @@ "grunt-contrib-csslint": "~0.1.2", "grunt-contrib-less": "~0.8.0", "grunt-cson": "0.14.0", - "grunt-download-atom-shell": "~0.12.0", + "grunt-download-atom-shell": "~0.14.0", "grunt-lesslint": "0.13.0", "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 3411f4dae..343960f61 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -26,6 +26,7 @@ module.exports = (grunt) -> cp 'package.json', path.join(appDir, 'package.json') + packageNames = [] packageDirectories = [] nonPackageDirectories = [ 'benchmark' @@ -39,6 +40,7 @@ module.exports = (grunt) -> directory = path.join('node_modules', child) if isAtomPackage(directory) packageDirectories.push(directory) + packageNames.push(child) else nonPackageDirectories.push(directory) @@ -61,6 +63,8 @@ module.exports = (grunt) -> path.join('npm', 'node_modules', '.bin', 'clear') path.join('npm', 'node_modules', '.bin', 'starwars') path.join('pegjs', 'examples') + path.join('get-parameter-names', 'node_modules', 'testla') + path.join('get-parameter-names', 'node_modules', '.bin', 'testla') path.join('jasmine-reporters', 'ext') path.join('jasmine-node', 'node_modules', 'gaze') path.join('jasmine-node', 'spec') @@ -79,19 +83,32 @@ module.exports = (grunt) -> path.join('resources', 'win') # These are only require in dev mode when the grammar isn't precompiled - path.join('atom-keymap', 'node_modules', 'loophole') - path.join('atom-keymap', 'node_modules', 'pegjs') - path.join('atom-keymap', 'node_modules', '.bin', 'pegjs') path.join('snippets', 'node_modules', 'loophole') path.join('snippets', 'node_modules', 'pegjs') path.join('snippets', 'node_modules', '.bin', 'pegjs') + # These aren't needed since WeakMap is built-in + path.join('emissary', 'node_modules', 'es6-weak-map') + path.join('property-accessors', 'node_modules', 'es6-weak-map') + '.DS_Store' '.jshintrc' '.npmignore' '.pairs' '.travis.yml' + 'appveyor.yml' + '.idea' + '.editorconfig' + '.lint' + '.lintignore' + '.eslintrc' + '.jshintignore' + '.gitattributes' + '.gitkeep' ] + + packageNames.forEach (packageName) -> ignoredPaths.push(path.join(packageName, 'spec')) + ignoredPaths = ignoredPaths.map (ignoredPath) -> _.escapeRegExp(ignoredPath) # Add .* to avoid matching hunspell_dictionaries. @@ -108,6 +125,7 @@ module.exports = (grunt) -> ignoredPaths.push "#{_.escapeRegExp(path.join('runas', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('scrollbar-style', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{_.escapeRegExp(path.join('spellchecker', 'src') + path.sep)}.*\\.(cc|h)*" + ignoredPaths.push "#{_.escapeRegExp(path.join('keyboard-layout', 'src') + path.sep)}.*\\.(cc|h|mm)*" # Ignore build files ignoredPaths.push "#{_.escapeRegExp(path.sep)}binding\\.gyp$" @@ -120,7 +138,7 @@ module.exports = (grunt) -> ignoredPaths.push path.join('spellchecker', 'vendor', 'hunspell_dictionaries') ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})" - testFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}te?sts?#{_.escapeRegExp(path.sep)}") + testFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}_*te?sts?_*#{_.escapeRegExp(path.sep)}") exampleFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}examples?#{_.escapeRegExp(path.sep)}") benchmarkFolderPattern = new RegExp("#{_.escapeRegExp(path.sep)}benchmarks?#{_.escapeRegExp(path.sep)}") @@ -144,7 +162,6 @@ module.exports = (grunt) -> for directory in packageDirectories cp directory, path.join(appDir, directory), filter: filterPackage - cp 'spec', path.join(appDir, 'spec'), filter: /fixtures|integration|.+-spec\.coffee/ cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/ cp 'static', path.join(appDir, 'static') diff --git a/build/tasks/compile-packages-slug-task.coffee b/build/tasks/compile-packages-slug-task.coffee index 613a4a380..a56ed6e18 100644 --- a/build/tasks/compile-packages-slug-task.coffee +++ b/build/tasks/compile-packages-slug-task.coffee @@ -54,15 +54,19 @@ module.exports = (grunt) -> mainPath = require.resolve(path.resolve(moduleDirectory, metadata.main)) pack.main = path.relative(appDir, mainPath) - for keymapPath in fs.listSync(path.join(moduleDirectory, 'keymaps'), ['.cson', '.json']) + keymapsPath = path.join(moduleDirectory, 'keymaps') + for keymapPath in fs.listSync(keymapsPath, ['.cson', '.json']) relativePath = path.relative(appDir, keymapPath) pack.keymaps[relativePath] = CSON.readFileSync(keymapPath) rm keymapPath + rm keymapsPath if fs.listSync(keymapsPath).length is 0 - for menuPath in fs.listSync(path.join(moduleDirectory, 'menus'), ['.cson', '.json']) + menusPath = path.join(moduleDirectory, 'menus') + for menuPath in fs.listSync(menusPath, ['.cson', '.json']) relativePath = path.relative(appDir, menuPath) pack.menus[relativePath] = CSON.readFileSync(menuPath) rm menuPath + rm menusPath if fs.listSync(menusPath).length is 0 packages[metadata.name] = pack diff --git a/build/tasks/output-build-filetypes.coffee b/build/tasks/output-build-filetypes.coffee index 64b952bbe..1b8aeb330 100644 --- a/build/tasks/output-build-filetypes.coffee +++ b/build/tasks/output-build-filetypes.coffee @@ -1,3 +1,4 @@ +asar = require 'asar' path = require 'path' module.exports = (grunt) -> @@ -5,17 +6,29 @@ module.exports = (grunt) -> shellAppDir = grunt.config.get('atom.shellAppDir') types = {} - grunt.file.recurse shellAppDir, (absolutePath, rootPath, relativePath, fileName) -> - extension = path.extname(fileName) or fileName - types[extension] ?= 0 - types[extension]++ + registerFile = (filePath) -> + extension = path.extname(filePath) or path.basename(filePath) + types[extension] ?= [] + types[extension].push(filePath) + + if extension is '.asar' + asar.listPackage(filePath).forEach (archivePath) -> + archivePath = archivePath.substring(1) + unless asar.statFile(filePath, archivePath, true).files + registerFile(archivePath) + + grunt.file.recurse shellAppDir, (absolutePath, rootPath, relativePath, fileName) -> registerFile(absolutePath) extensions = Object.keys(types).sort (extension1, extension2) -> - diff = types[extension2] - types[extension1] + diff = types[extension2].length - types[extension1].length if diff is 0 extension1.toLowerCase().localeCompare(extension2.toLowerCase()) else diff - extensions.forEach (extension) -> - grunt.log.error "#{extension}: #{types[extension]}" + if extension = grunt.option('extension') + types[extension]?.sort().forEach (filePath) -> + grunt.log.error filePath + else + extensions[0...25].forEach (extension) -> + grunt.log.error "#{extension}: #{types[extension].length}" diff --git a/package.json b/package.json index 1fcb6c185..29be99f96 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "emissary": "^1.3.3", "event-kit": "^1.1", "first-mate": "^3.1", - "fs-plus": "^2.6", + "fs-plus": "^2.7.1", "fstream": "0.1.24", "fuzzaldrin": "^2.1", "git-utils": "^3.0.0", @@ -106,11 +106,11 @@ "link": "0.30.0", "markdown-preview": "0.148.0", "metrics": "0.45.0", - "notifications": "0.38.0", + "notifications": "0.39.0", "open-on-github": "0.36.0", "package-generator": "0.38.0", "release-notes": "0.52.0", - "settings-view": "0.192.0", + "settings-view": "0.193.0", "snippets": "0.88.0", "spell-check": "0.55.0", "status-bar": "0.69.0", diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 4aeb46c68..2c5ba2fc2 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -486,6 +486,7 @@ describe "Config", -> atom.config.set('foo.bar.baz', "value 1") expect(observeHandler).toHaveBeenCalledWith("value 1") + advanceClock(100) # complete pending save that was requested in ::set observeHandler.reset() atom.config.loadUserConfig() @@ -789,6 +790,22 @@ describe "Config", -> expect(warnSpy).toHaveBeenCalled() expect(warnSpy.mostRecentCall.args[0]).toContain "foo.int" + describe "when there is a pending save", -> + it "does not change the config settings", -> + fs.writeFileSync atom.config.configFilePath, "'*': foo: bar: 'baz'" + + atom.config.set("foo.bar", "quux") + atom.config.loadUserConfig() + expect(atom.config.get("foo.bar")).toBe "quux" + + advanceClock(100) + expect(atom.config.save.callCount).toBe 1 + + expect(atom.config.get("foo.bar")).toBe "quux" + atom.config.loadUserConfig() + expect(atom.config.get("foo.bar")).toBe "baz" + + describe ".observeUserConfig()", -> updatedHandler = null @@ -854,7 +871,7 @@ describe "Config", -> expect(atom.config.get('foo.bar')).toBe 'baz' expect(atom.config.get('foo.baz')).toBe 'ok' - describe 'when the default value is a complex value', -> + describe "when the default value is a complex value", -> beforeEach -> atom.config.setSchema 'foo.bar', type: 'array' @@ -877,7 +894,7 @@ describe "Config", -> expect(atom.config.get('foo.bar')).toEqual ['baz', 'ok'] expect(atom.config.get('foo.baz')).toBe 'another' - describe 'when scoped settings are used', -> + describe "when scoped settings are used", -> it "fires a change event for scoped settings that are removed", -> scopedSpy = jasmine.createSpy() atom.config.onDidChange('foo.scoped', scope: ['.source.ruby'], scopedSpy) diff --git a/src/config.coffee b/src/config.coffee index 6c440f219..b97ce99ec 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -332,9 +332,16 @@ class Config @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) @configFilePath ?= path.join(@configDirPath, 'config.cson') @transactDepth = 0 + @savePending = false - @debouncedSave = _.debounce(@save, 100) - @debouncedLoad = _.debounce(@loadUserConfig, 100) + @requestLoad = _.debounce(@loadUserConfig, 100) + @requestSave = => + @savePending = true + debouncedSave.call(this) + save = => + @savePending = false + @save() + debouncedSave = _.debounce(save, 100) ### Section: Config Subscription @@ -605,7 +612,7 @@ class Config else @setRawValue(keyPath, value) - @debouncedSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors + @requestSave() if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors true # Essential: Restore the setting at `keyPath` to its default value. @@ -635,7 +642,7 @@ class Config _.setValueForKeyPath(settings, keyPath, undefined) settings = withoutEmptyObjects(settings) @set(null, settings, {scopeSelector, source, priority: @priorityForSource(source)}) if settings? - @debouncedSave() + @requestSave() else @scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) @emitChangeEvent() @@ -757,9 +764,10 @@ class Config CSON.writeFileSync(@configFilePath, {}) try - userConfig = CSON.readFileSync(@configFilePath) - @resetUserSettings(userConfig) - @configFileHasErrors = false + unless @savePending + userConfig = CSON.readFileSync(@configFilePath) + @resetUserSettings(userConfig) + @configFileHasErrors = false catch error @configFileHasErrors = true message = "Failed to load `#{path.basename(@configFilePath)}`" @@ -776,7 +784,7 @@ class Config observeUserConfig: -> try @watchSubscription ?= pathWatcher.watch @configFilePath, (eventType) => - @debouncedLoad() if eventType is 'change' and @watchSubscription? + @requestLoad() if eventType is 'change' and @watchSubscription? catch error @notifyFailure """ Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to diff --git a/static/variables/syntax-variables.less b/static/variables/syntax-variables.less index f569773e8..608943cf4 100644 --- a/static/variables/syntax-variables.less +++ b/static/variables/syntax-variables.less @@ -39,5 +39,6 @@ @syntax-color-class: #E5C17C; @syntax-color-keyword: #555; @syntax-color-tag: #555; +@syntax-color-attribute: #87400d; @syntax-color-import: #97C378; @syntax-color-snippet: #97C378;