From b6829f08fa21f40b717bf4e800cd11906b04c9ab Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 7 Mar 2017 17:56:36 +0100 Subject: [PATCH 01/17] Temporarily add faster script to build Atom --- script/lib/fast-clean-output-directory.js | 19 +++++++++ script/lib/fast-copy-assets.js | 31 ++++++++++++++ script/lib/fast-transpile-babel-paths.js | 36 ++++++++++++++++ .../lib/fast-transpile-coffee-script-paths.js | 28 +++++++++++++ script/tdd | 42 +++++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 script/lib/fast-clean-output-directory.js create mode 100644 script/lib/fast-copy-assets.js create mode 100644 script/lib/fast-transpile-babel-paths.js create mode 100644 script/lib/fast-transpile-coffee-script-paths.js create mode 100755 script/tdd diff --git a/script/lib/fast-clean-output-directory.js b/script/lib/fast-clean-output-directory.js new file mode 100644 index 000000000..b1053eba2 --- /dev/null +++ b/script/lib/fast-clean-output-directory.js @@ -0,0 +1,19 @@ +const fs = require('fs-extra') +const path = require('path') +const CONFIG = require('../config') + +module.exports = function () { + let srcPaths = [ + path.join('benchmarks', 'benchmark-runner.js'), + path.join('dot-atom'), + path.join('exports'), + path.join('package.json'), + path.join('static'), + path.join('src'), + path.join('vendor') + ] + + for (const srcPath of srcPaths) { + fs.removeSync(path.join(CONFIG.intermediateAppPath, srcPath)) + } +} diff --git a/script/lib/fast-copy-assets.js b/script/lib/fast-copy-assets.js new file mode 100644 index 000000000..1602a7af8 --- /dev/null +++ b/script/lib/fast-copy-assets.js @@ -0,0 +1,31 @@ +// This module exports a function that copies all the static assets into the +// appropriate location in the build output directory. + +'use strict' + +const path = require('path') +const fs = require('fs-extra') +const CONFIG = require('../config') +const glob = require('glob') +const includePathInPackagedApp = require('./include-path-in-packaged-app') + +module.exports = function () { + console.log(`Copying assets to ${CONFIG.intermediateAppPath}`); + let srcPaths = [ + path.join(CONFIG.repositoryRootPath, 'benchmarks', 'benchmark-runner.js'), + path.join(CONFIG.repositoryRootPath, 'dot-atom'), + path.join(CONFIG.repositoryRootPath, 'exports'), + path.join(CONFIG.repositoryRootPath, 'package.json'), + path.join(CONFIG.repositoryRootPath, 'static'), + path.join(CONFIG.repositoryRootPath, 'src'), + path.join(CONFIG.repositoryRootPath, 'vendor') + ] + for (let srcPath of srcPaths) { + fs.copySync(srcPath, computeDestinationPath(srcPath), {filter: includePathInPackagedApp}) + } +} + +function computeDestinationPath (srcPath) { + const relativePath = path.relative(CONFIG.repositoryRootPath, srcPath) + return path.join(CONFIG.intermediateAppPath, relativePath) +} diff --git a/script/lib/fast-transpile-babel-paths.js b/script/lib/fast-transpile-babel-paths.js new file mode 100644 index 000000000..a1901e7a0 --- /dev/null +++ b/script/lib/fast-transpile-babel-paths.js @@ -0,0 +1,36 @@ +'use strict' + +const CompileCache = require('../../src/compile-cache') +const fs = require('fs') +const glob = require('glob') +const path = require('path') + +const CONFIG = require('../config') +const BABEL_OPTIONS = require('../../static/babelrc.json') +const BABEL_PREFIXES = [ + "'use babel'", + '"use babel"', + '/** @babel */', + '/* @flow */' +] +const PREFIX_LENGTH = Math.max.apply(null, BABEL_PREFIXES.map(prefix => prefix.length)) +const BUFFER = Buffer(PREFIX_LENGTH) + +module.exports = function () { + console.log(`Transpiling Babel paths in ${CONFIG.intermediateAppPath}`) + for (let path of getPathsToTranspile()) { + transpileBabelPath(path) + } +} + +function getPathsToTranspile () { + let paths = [] + paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'benchmarks', '**', '*.js'))) + paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'exports', '**', '*.js'))) + paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'src', '**', '*.js'))) + return paths +} + +function transpileBabelPath (path) { + fs.writeFileSync(path, CompileCache.addPathToCache(path, CONFIG.atomHomeDirPath)) +} diff --git a/script/lib/fast-transpile-coffee-script-paths.js b/script/lib/fast-transpile-coffee-script-paths.js new file mode 100644 index 000000000..7e080691f --- /dev/null +++ b/script/lib/fast-transpile-coffee-script-paths.js @@ -0,0 +1,28 @@ +'use strict' + +const CompileCache = require('../../src/compile-cache') +const fs = require('fs') +const glob = require('glob') +const path = require('path') + +const CONFIG = require('../config') + +module.exports = function () { + console.log(`Transpiling CoffeeScript paths in ${CONFIG.intermediateAppPath}`) + for (let path of getPathsToTranspile()) { + transpileCoffeeScriptPath(path) + } +} + +function getPathsToTranspile () { + let paths = [] + paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'src', '**', '*.coffee'))) + paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'spec', '*.coffee'))) + return paths +} + +function transpileCoffeeScriptPath (coffeePath) { + const jsPath = coffeePath.replace(/coffee$/g, 'js') + fs.writeFileSync(jsPath, CompileCache.addPathToCache(coffeePath, CONFIG.atomHomeDirPath)) + fs.unlinkSync(coffeePath) +} diff --git a/script/tdd b/script/tdd new file mode 100755 index 000000000..db78ef3e4 --- /dev/null +++ b/script/tdd @@ -0,0 +1,42 @@ +#!/usr/bin/env node + +'use strict' + +// Needed so we can require src/module-cache.coffee during generateModuleCache +require('coffee-script/register') +require('colors') + +const yargs = require('yargs') +const argv = yargs + .usage('Usage: $0 [options]') + .help('help') + .describe('code-sign', 'Code-sign executables (macOS and Windows only)') + .describe('create-windows-installer', 'Create installer (Windows only)') + .describe('create-debian-package', 'Create .deb package (Linux only)') + .describe('create-rpm-package', 'Create .rpm package (Linux only)') + .describe('compress-artifacts', 'Compress Atom binaries (and symbols on macOS)') + .describe('install', 'Install Atom') + .wrap(yargs.terminalWidth()) + .argv + +const cleanOutputDirectory = require('./lib/fast-clean-output-directory') +const copyAssets = require('./lib/fast-copy-assets') +const generateMetadata = require('./lib/generate-metadata') +const generateModuleCache = require('./lib/generate-module-cache') +const generateStartupSnapshot = require('./lib/generate-startup-snapshot') +const packageApplication = require('./lib/package-application') +const transpileBabelPaths = require('./lib/fast-transpile-babel-paths') +const transpileCoffeeScriptPaths = require('./lib/fast-transpile-coffee-script-paths') + +process.on('unhandledRejection', function (e) { + console.error(e.stack || e) + process.exit(1) +}) + +cleanOutputDirectory() +copyAssets() +transpileBabelPaths() +transpileCoffeeScriptPaths() +generateModuleCache() +generateMetadata() +packageApplication().then(generateStartupSnapshot) From 41c9e35fcc58d29fdb727123e76fce7a1a764127 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 7 Mar 2017 18:07:48 +0100 Subject: [PATCH 02/17] Start moving AtomEnvironment instantiation bits inside the snapshot --- src/atom-environment.coffee | 22 ++++++++++++---------- src/config.coffee | 6 ++++-- src/initialize-application-window.coffee | 18 +++++++++++------- src/view-registry.coffee | 12 +++++++++--- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index b642cbe5f..f4ae97c53 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -131,14 +131,21 @@ class AtomEnvironment extends Model # Call .loadOrCreate instead constructor: (params={}) -> - {@applicationDelegate, @window, @document, @blobStore, @clipboard, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + {@applicationDelegate, @clipboard, @enablePersistence, onlyLoadBaseStyleSheets} = params @unloaded = false @loadTime = null - {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() - @emitter = new Emitter @disposables = new CompositeDisposable + @deserializers = new DeserializerManager(this) + @deserializeTimings = {} + @views = new ViewRegistry(this) + @notifications = new NotificationManager + @config = new Config({notificationManager: @notifications, @enablePersistence}) + + initialize: (params={}) -> + {@applicationDelegate, @window, @document, @blobStore, @clipboard, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() @stateStore = new StateStore('AtomEnvironments', 1) @@ -146,14 +153,9 @@ class AtomEnvironment extends Model @getStorageFolder().clear() @stateStore.clear() - @deserializers = new DeserializerManager(this) - @deserializeTimings = {} + @views.initialize() - @views = new ViewRegistry(this) - - @notifications = new NotificationManager - - @config = new Config({@configDirPath, resourcePath, notificationManager: @notifications, @enablePersistence}) + @config.initialize({@configDirPath, resourcePath}) @setConfigSchema() @keymaps = new KeymapManager({@configDirPath, resourcePath, notificationManager: @notifications}) diff --git a/src/config.coffee b/src/config.coffee index e7d05da6d..575e95a72 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -398,11 +398,13 @@ class Config value # Created during initialization, available as `atom.config` - constructor: ({@configDirPath, @resourcePath, @notificationManager, @enablePersistence}={}) -> + constructor: ({@notificationManager, @enablePersistence}={}) -> + @clear() + + initialize: ({@configDirPath, @resourcePath}) -> if @enablePersistence? @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) @configFilePath ?= path.join(@configDirPath, 'config.cson') - @clear() clear: -> @emitter = new Emitter diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index f0ea0ed12..076b1bf32 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -55,6 +55,15 @@ require('welcome') require('whitespace') require('wrap-guide') +clipboard = new Clipboard +TextEditor.setClipboard(clipboard) + +window.atom = new AtomEnvironment({ + clipboard, + applicationDelegate: new ApplicationDelegate, + enablePersistence: true +}) + # Like sands through the hourglass, so are the days of our lives. module.exports = ({blobStore}) -> {updateProcessEnv} = require('./update-process-env') @@ -73,14 +82,9 @@ module.exports = ({blobStore}) -> # Make React faster process.env.NODE_ENV ?= 'production' unless devMode - clipboard = new Clipboard - TextEditor.setClipboard(clipboard) - - window.atom = new AtomEnvironment({ - window, document, clipboard, blobStore, - applicationDelegate: new ApplicationDelegate, + window.atom.initialize({ + window, document, blobStore, configDirPath: process.env.ATOM_HOME, - enablePersistence: true, env: process.env }) diff --git a/src/view-registry.coffee b/src/view-registry.coffee index e2cd986dc..5a8e72a4c 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -54,9 +54,12 @@ class ViewRegistry minimumPollInterval: 200 constructor: (@atomEnvironment) -> - @observer = new MutationObserver(@requestDocumentPoll) + @polling = false @clear() + initialize: -> + @observer = new MutationObserver(@requestDocumentPoll) + clear: -> @views = new WeakMap @providers = [] @@ -267,10 +270,13 @@ class ViewRegistry startPollingDocument: -> window.addEventListener('resize', @requestDocumentPoll) @observer.observe(document, {subtree: true, childList: true, attributes: true}) + @polling = true stopPollingDocument: -> - window.removeEventListener('resize', @requestDocumentPoll) - @observer.disconnect() + if @polling + window.removeEventListener('resize', @requestDocumentPoll) + @observer.disconnect() + @polling = false requestDocumentPoll: => if @animationFrameRequest? From 21de78ce38a8e2e9c23bc4719b52522f5a3c5f96 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 7 Mar 2017 18:30:38 +0100 Subject: [PATCH 03/17] Snapshot config, keymaps, tooltips, commands, grammars and styles --- script/lib/generate-startup-snapshot.js | 1 - src/atom-environment.coffee | 39 ++++++++++++++++-------- src/config-schema.js | 12 +------- src/main-process/atom-application.coffee | 3 +- src/style-manager.js | 13 +++++--- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/script/lib/generate-startup-snapshot.js b/script/lib/generate-startup-snapshot.js index 0021cf1ab..be3f06839 100644 --- a/script/lib/generate-startup-snapshot.js +++ b/script/lib/generate-startup-snapshot.js @@ -27,7 +27,6 @@ module.exports = function (packagedAppPath) { coreModules.has(modulePath) || (relativePath.startsWith(path.join('..', 'src')) && relativePath.endsWith('-element.js')) || relativePath == path.join('..', 'exports', 'atom.js') || - relativePath == path.join('..', 'src', 'config-schema.js') || relativePath == path.join('..', 'src', 'electron-shims.js') || relativePath == path.join('..', 'src', 'safe-clipboard.js') || relativePath == path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') || diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index f4ae97c53..8aa79736d 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -13,6 +13,7 @@ StateStore = require './state-store' StorageFolder = require './storage-folder' registerDefaultCommands = require './register-default-commands' {updateProcessEnv} = require './update-process-env' +ConfigSchema = require './config-schema' DeserializerManager = require './deserializer-manager' ViewRegistry = require './view-registry' @@ -141,10 +142,18 @@ class AtomEnvironment extends Model @deserializeTimings = {} @views = new ViewRegistry(this) @notifications = new NotificationManager + @config = new Config({notificationManager: @notifications, @enablePersistence}) + @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} + + @keymaps = new KeymapManager({notificationManager: @notifications}) + @tooltips = new TooltipManager(keymapManager: @keymaps, viewRegistry: @views) + @commands = new CommandRegistry + @grammars = new GrammarRegistry({@config}) + @styles = new StyleManager() initialize: (params={}) -> - {@applicationDelegate, @window, @document, @blobStore, @clipboard, @configDirPath, @enablePersistence, onlyLoadBaseStyleSheets} = params + {@window, @document, @blobStore, @configDirPath, onlyLoadBaseStyleSheets} = params {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() @stateStore = new StateStore('AtomEnvironments', 1) @@ -156,18 +165,24 @@ class AtomEnvironment extends Model @views.initialize() @config.initialize({@configDirPath, resourcePath}) - @setConfigSchema() + @projectHomeSchema = { + type: 'object', + properties: { + projectHome: { + type: 'string', + default: path.join(fs.getHomeDirectory(), 'github'), + description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' + } + } + } + @config.setSchema('core', @projectHomeSchema) - @keymaps = new KeymapManager({@configDirPath, resourcePath, notificationManager: @notifications}) + @keymaps.configDirPath = @configDirPath + @keymaps.resourcePath = resourcePath - @tooltips = new TooltipManager(keymapManager: @keymaps, viewRegistry: @views) - - @commands = new CommandRegistry @commands.attach(@window) - @grammars = new GrammarRegistry({@config}) - - @styles = new StyleManager({@configDirPath}) + @styles.initialize({@configDirPath}) @packages = new PackageManager({ devMode, @configDirPath, resourcePath, safeMode, @config, styleManager: @styles, @@ -247,9 +262,6 @@ class AtomEnvironment extends Model @document.removeEventListener('mousedown', saveState, true) @document.removeEventListener('keydown', saveState, true) - setConfigSchema: -> - @config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))} - registerDefaultDeserializers: -> @deserializers.add(Workspace) @deserializers.add(PaneContainer) @@ -302,7 +314,8 @@ class AtomEnvironment extends Model @registerDefaultDeserializers() @config.clear() - @setConfigSchema() + @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} + @config.setSchema('core', @projectHomeSchema) @keymaps.clear() @keymaps.loadBundledKeymaps() diff --git a/src/config-schema.js b/src/config-schema.js index 41b5ecbb6..b19624a38 100644 --- a/src/config-schema.js +++ b/src/config-schema.js @@ -1,8 +1,3 @@ -/** @babel */ - -import path from 'path' -import fs from 'fs-plus' - // This is loaded by atom-environment.coffee. See // https://atom.io/docs/api/latest/Config for more information about config // schemas. @@ -58,11 +53,6 @@ const configSchema = { }, description: 'Names of UI and syntax themes which will be used when Atom starts.' }, - projectHome: { - type: 'string', - default: path.join(fs.getHomeDirectory(), 'github'), - description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' - }, audioBeep: { type: 'boolean', default: true, @@ -506,4 +496,4 @@ if (process.platform === 'darwin') { } } -export default configSchema +module.exports = configSchema diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 295343100..73eb5a134 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -68,7 +68,8 @@ class AtomApplication @pidsToOpenWindows = {} @windows = [] - @config = new Config({configDirPath: process.env.ATOM_HOME, @resourcePath, enablePersistence: true}) + @config = new Config({enablePersistence: true}) + @config.initialize({configDirPath: process.env.ATOM_HOME, @resourcePath}) @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))} @config.load() @fileRecoveryService = new FileRecoveryService(path.join(process.env.ATOM_HOME, "recovery")) diff --git a/src/style-manager.js b/src/style-manager.js index 0a0b401d3..718c1ee74 100644 --- a/src/style-manager.js +++ b/src/style-manager.js @@ -13,17 +13,20 @@ const DEPRECATED_SYNTAX_SELECTORS = require('./deprecated-syntax-selectors') // own, but is instead subscribed to by individual `` elements, // which clone and attach style elements in different contexts. module.exports = class StyleManager { - constructor ({configDirPath}) { - this.configDirPath = configDirPath - if (this.configDirPath != null) { - this.cacheDirPath = path.join(this.configDirPath, 'compile-cache', 'style-manager') - } + constructor () { this.emitter = new Emitter() this.styleElements = [] this.styleElementsBySourcePath = {} this.deprecationsBySourcePath = {} } + initialize ({configDirPath}) { + this.configDirPath = configDirPath + if (this.configDirPath != null) { + this.cacheDirPath = path.join(this.configDirPath, 'compile-cache', 'style-manager') + } + } + /* Section: Event Subscription */ From 3d4fdd051d1f714e5f77c3250022e037291788c7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2017 10:02:35 +0100 Subject: [PATCH 04/17] Snapshot more objects --- package.json | 1 + src/atom-environment.coffee | 105 ++++++++++++++------------- src/auto-update-manager.js | 12 +-- src/command-installer.coffee | 4 +- src/command-registry.coffee | 9 ++- src/context-menu-manager.coffee | 29 +++++--- src/history-manager.js | 9 ++- src/keymap-extensions.coffee | 6 +- src/menu-manager.coffee | 9 ++- src/package-manager.coffee | 17 +++-- src/pane-container.coffee | 4 +- src/register-default-commands.coffee | 14 ++-- src/theme-manager.coffee | 4 +- src/window-event-handler.coffee | 37 +++++----- src/workspace.js | 68 +++++++++-------- 15 files changed, 181 insertions(+), 147 deletions(-) diff --git a/package.json b/package.json index 54939847f..63f71bf4a 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "normalize-package-data": "^2.0.0", "nslog": "^3", "oniguruma": "6.1.0", + "parserlib": "^1.1.1", "pathwatcher": "7.0.0", "postcss": "5.2.4", "postcss-selector-parser": "2.2.1", diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8aa79736d..2c5398d64 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -151,6 +151,51 @@ class AtomEnvironment extends Model @commands = new CommandRegistry @grammars = new GrammarRegistry({@config}) @styles = new StyleManager() + @packages = new PackageManager({ + @config, styleManager: @styles, + commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications, + grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views + }) + @themes = new ThemeManager({ + packageManager: @packages, @config, styleManager: @styles, + notificationManager: @notifications, viewRegistry: @views + }) + @menu = new MenuManager({keymapManager: @keymaps, packageManager: @packages}) + @contextMenu = new ContextMenuManager({keymapManager: @keymaps}) + @packages.setMenuManager(@menu) + @packages.setContextMenuManager(@contextMenu) + @packages.setThemeManager(@themes) + + @project = new Project({notificationManager: @notifications, packageManager: @packages, @config, @applicationDelegate}) + @commandInstaller = new CommandInstaller(@applicationDelegate) + + @textEditors = new TextEditorRegistry({ + @config, grammarRegistry: @grammars, assert: @assert.bind(this), + packageManager: @packages + }) + + @workspace = new Workspace({ + @config, @project, packageManager: @packages, grammarRegistry: @grammars, deserializerManager: @deserializers, + notificationManager: @notifications, @applicationDelegate, viewRegistry: @views, assert: @assert.bind(this), + textEditorRegistry: @textEditors, + }) + + @themes.workspace = @workspace + + @autoUpdater = new AutoUpdateManager({@applicationDelegate}) + + @keymaps.loadBundledKeymaps() + @registerDefaultCommands() + @registerDefaultOpeners() + @registerDefaultDeserializers() + @registerDefaultViewProviders() + + @windowEventHandler = new WindowEventHandler({atomEnvironment: this, @applicationDelegate}) + + @history = new HistoryManager({@project, @commands}) + # Keep instances of HistoryManager in sync + @disposables.add @history.onDidChangeProjects (e) => + @applicationDelegate.didChangeHistoryManager() unless e.reloaded initialize: (params={}) -> {@window, @document, @blobStore, @configDirPath, onlyLoadBaseStyleSheets} = params @@ -183,44 +228,14 @@ class AtomEnvironment extends Model @commands.attach(@window) @styles.initialize({@configDirPath}) + @packages.initialize({devMode, @configDirPath, resourcePath, safeMode}) + @themes.initialize({@configDirPath, resourcePath, safeMode}) - @packages = new PackageManager({ - devMode, @configDirPath, resourcePath, safeMode, @config, styleManager: @styles, - commandRegistry: @commands, keymapManager: @keymaps, notificationManager: @notifications, - grammarRegistry: @grammars, deserializerManager: @deserializers, viewRegistry: @views - }) - - @themes = new ThemeManager({ - packageManager: @packages, @configDirPath, resourcePath, safeMode, @config, - styleManager: @styles, notificationManager: @notifications, viewRegistry: @views - }) - - @menu = new MenuManager({resourcePath, keymapManager: @keymaps, packageManager: @packages}) - - @contextMenu = new ContextMenuManager({resourcePath, devMode, keymapManager: @keymaps}) - - @packages.setMenuManager(@menu) - @packages.setContextMenuManager(@contextMenu) - @packages.setThemeManager(@themes) - - @project = new Project({notificationManager: @notifications, packageManager: @packages, @config, @applicationDelegate}) - - @commandInstaller = new CommandInstaller(@getVersion(), @applicationDelegate) - - @textEditors = new TextEditorRegistry({ - @config, grammarRegistry: @grammars, assert: @assert.bind(this), - packageManager: @packages - }) - - @workspace = new Workspace({ - @config, @project, packageManager: @packages, grammarRegistry: @grammars, deserializerManager: @deserializers, - notificationManager: @notifications, @applicationDelegate, viewRegistry: @views, assert: @assert.bind(this), - textEditorRegistry: @textEditors, - }) - - @themes.workspace = @workspace - - @autoUpdater = new AutoUpdateManager({@applicationDelegate}) + @menu.initialize({resourcePath}) + @contextMenu.initialize({resourcePath, devMode}) + @commandInstaller.initialize(@getVersion()) + @workspace.initialize() + @autoUpdater.initialize() @config.load() @@ -233,23 +248,14 @@ class AtomEnvironment extends Model @document.head.appendChild(@stylesElement) @keymaps.subscribeToFileReadFailure() - @keymaps.loadBundledKeymaps() - - @registerDefaultCommands() - @registerDefaultOpeners() - @registerDefaultDeserializers() - @registerDefaultViewProviders() @installUncaughtErrorHandler() @attachSaveStateListeners() - @installWindowEventHandler() + @windowEventHandler.initialize(@window, @document) @observeAutoHideMenuBar() - @history = new HistoryManager({@project, @commands, @stateStore}) - # Keep instances of HistoryManager in sync - @disposables.add @history.onDidChangeProjects (e) => - @applicationDelegate.didChangeHistoryManager() unless e.reloaded + @history.initialize(@stateStore) @disposables.add @applicationDelegate.onDidChangeHistoryManager(=> @history.loadState()) attachSaveStateListeners: -> @@ -786,9 +792,6 @@ class AtomEnvironment extends Model uninstallUncaughtErrorHandler: -> @window.onerror = @previousWindowErrorHandler - installWindowEventHandler: -> - @windowEventHandler = new WindowEventHandler({atomEnvironment: this, @applicationDelegate, @window, @document}) - uninstallWindowEventHandler: -> @windowEventHandler?.unsubscribe() diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index fb6325a26..111147f32 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -7,21 +7,23 @@ export default class AutoUpdateManager { this.applicationDelegate = applicationDelegate this.subscriptions = new CompositeDisposable() this.emitter = new Emitter() + } + initialize () { this.subscriptions.add( - applicationDelegate.onDidBeginCheckingForUpdate(() => { + this.applicationDelegate.onDidBeginCheckingForUpdate(() => { this.emitter.emit('did-begin-checking-for-update') }), - applicationDelegate.onDidBeginDownloadingUpdate(() => { + this.applicationDelegate.onDidBeginDownloadingUpdate(() => { this.emitter.emit('did-begin-downloading-update') }), - applicationDelegate.onDidCompleteDownloadingUpdate((details) => { + this.applicationDelegate.onDidCompleteDownloadingUpdate((details) => { this.emitter.emit('did-complete-downloading-update', details) }), - applicationDelegate.onUpdateNotAvailable(() => { + this.applicationDelegate.onUpdateNotAvailable(() => { this.emitter.emit('update-not-available') }), - applicationDelegate.onUpdateError(() => { + this.applicationDelegate.onUpdateError(() => { this.emitter.emit('update-error') }) ) diff --git a/src/command-installer.coffee b/src/command-installer.coffee index e37e6a0e6..7873014fa 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -26,7 +26,9 @@ symlinkCommandWithPrivilegeSync = (sourcePath, destinationPath) -> module.exports = class CommandInstaller - constructor: (@appVersion, @applicationDelegate) -> + constructor: (@applicationDelegate) -> + + initialize: (@appVersion) -> getInstallDirectory: -> "/usr/local/bin" diff --git a/src/command-registry.coffee b/src/command-registry.coffee index 056446203..269714876 100644 --- a/src/command-registry.coffee +++ b/src/command-registry.coffee @@ -91,23 +91,26 @@ class CommandRegistry # # Returns a {Disposable} on which `.dispose()` can be called to remove the # added command handler(s). - add: (target, commandName, callback) -> + add: (target, commandName, callback, throwOnInvalidSelector = true) -> if typeof commandName is 'object' commands = commandName disposable = new CompositeDisposable for commandName, callback of commands - disposable.add @add(target, commandName, callback) + disposable.add @add(target, commandName, callback, throwOnInvalidSelector) return disposable if typeof callback isnt 'function' throw new Error("Can't register a command with non-function callback.") if typeof target is 'string' - validateSelector(target) + validateSelector(target) if throwOnInvalidSelector @addSelectorBasedListener(target, commandName, callback) else @addInlineListener(target, commandName, callback) + addBundled: (target, commandName, callback) -> + @add(target, commandName, callback, false) + addSelectorBasedListener: (selector, commandName, callback) -> @selectorBasedListenersByCommandName[commandName] ?= [] listenersForCommand = @selectorBasedListenersByCommandName[commandName] diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 4dc54cede..c15ca20a6 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -40,15 +40,17 @@ platformContextMenu = require('../package.json')?._atomMenu?['context-menu'] # {::add} for more information. module.exports = class ContextMenuManager - constructor: ({@resourcePath, @devMode, @keymapManager}) -> + constructor: ({@keymapManager}) -> @definitions = {'.overlayer': []} # TODO: Remove once color picker package stops touching private data @clear() @keymapManager.onDidLoadBundledKeymaps => @loadPlatformItems() + initialize: ({@resourcePath, @devMode}) -> + loadPlatformItems: -> if platformContextMenu? - @add(platformContextMenu) + @add(platformContextMenu, false) else menusDirPath = path.join(@resourcePath, 'menus') platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json']) @@ -107,11 +109,11 @@ class ContextMenuManager # # Returns a {Disposable} on which `.dispose()` can be called to remove the # added menu items. - add: (itemsBySelector) -> + add: (itemsBySelector, throwOnInvalidSelector = true) -> addedItemSets = [] for selector, items of itemsBySelector - validateSelector(selector) + validateSelector(selector) if throwOnInvalidSelector itemSet = new ContextMenuItemSet(selector, items) addedItemSets.push(itemSet) @itemSets.push(itemSet) @@ -206,14 +208,17 @@ class ContextMenuManager clear: -> @activeElement = null @itemSets = [] - @add 'atom-workspace': [{ - label: 'Inspect Element' - command: 'application:inspect' - devMode: true - created: (event) -> - {pageX, pageY} = event - @commandDetail = {x: pageX, y: pageY} - }] + inspectElement = { + 'atom-workspace': [{ + label: 'Inspect Element' + command: 'application:inspect' + devMode: true + created: (event) -> + {pageX, pageY} = event + @commandDetail = {x: pageX, y: pageY} + }] + } + @add(inspectElement, false) class ContextMenuItemSet constructor: (@selector, @items) -> diff --git a/src/history-manager.js b/src/history-manager.js index 5087c3bf9..ff3fbc218 100644 --- a/src/history-manager.js +++ b/src/history-manager.js @@ -8,15 +8,18 @@ import {Emitter, CompositeDisposable} from 'event-kit' // // The project history is used to enable the 'Reopen Project' menu. export class HistoryManager { - constructor ({stateStore, project, commands}) { - this.stateStore = stateStore + constructor ({project, commands}) { this.emitter = new Emitter() this.projects = [] this.disposables = new CompositeDisposable() - this.disposables.add(commands.add('atom-workspace', {'application:clear-project-history': this.clearProjects.bind(this)})) + this.disposables.add(commands.addBundled('atom-workspace', {'application:clear-project-history': this.clearProjects.bind(this)})) this.disposables.add(project.onDidChangePaths((projectPaths) => this.addProject(projectPaths))) } + initialize (stateStore) { + this.stateStore = stateStore + } + destroy () { this.disposables.dispose() } diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index bf8302f4c..76b2e8958 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -12,12 +12,12 @@ KeymapManager::onDidLoadUserKeymap = (callback) -> @emitter.on 'did-load-user-keymap', callback KeymapManager::loadBundledKeymaps = -> - keymapsPath = path.join(@resourcePath, 'keymaps') if bundledKeymaps? for keymapName, keymap of bundledKeymaps - keymapPath = path.join(keymapsPath, keymapName) - @add(keymapPath, keymap) + keymapPath = "/#{keymapName}" + @add(keymapPath, keymap, 0, false) else + keymapsPath = path.join(@resourcePath, 'keymaps') @loadKeymap(keymapsPath) @emitter.emit 'did-load-bundled-keymaps' diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index b6ed7fd1a..18dd49c5a 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -60,12 +60,17 @@ platformMenu = require('../package.json')?._atomMenu?.menu module.exports = class MenuManager constructor: ({@resourcePath, @keymapManager, @packageManager}) -> + @initialized = false @pendingUpdateOperation = null @template = [] @keymapManager.onDidLoadBundledKeymaps => @loadPlatformItems() - @keymapManager.onDidReloadKeymap => @update() @packageManager.onDidActivateInitialPackages => @sortPackagesMenu() + initialize: ({@resourcePath}) -> + @keymapManager.onDidReloadKeymap => @update() + @update() + @initialized = true + # Public: Adds the given items to the application menu. # # ## Examples @@ -89,7 +94,7 @@ class MenuManager add: (items) -> items = _.deepClone(items) @merge(@template, item) for item in items - @update() + @update() if @initialized new Disposable => @remove(items) remove: (items) -> diff --git a/src/package-manager.coffee b/src/package-manager.coffee index fb4f7a658..a7b068bbf 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -30,9 +30,8 @@ module.exports = class PackageManager constructor: (params) -> { - configDirPath, @devMode, safeMode, @resourcePath, @config, @styleManager, - @notificationManager, @keymapManager, @commandRegistry, @grammarRegistry, - @deserializerManager, @viewRegistry + @config, @styleManager, @notificationManager, @keymapManager, + @commandRegistry, @grammarRegistry, @deserializerManager, @viewRegistry } = params @emitter = new Emitter @@ -40,11 +39,6 @@ class PackageManager @packageDirPaths = [] @deferredActivationHooks = [] @triggeredActivationHooks = new Set() - if configDirPath? and not safeMode - if @devMode - @packageDirPaths.push(path.join(configDirPath, "dev", "packages")) - @packageDirPaths.push(path.join(configDirPath, "packages")) - @packagesCache = require('../package.json')?._atomPackages ? {} @initialPackagesLoaded = false @initialPackagesActivated = false @@ -57,6 +51,13 @@ class PackageManager @packageActivators = [] @registerPackageActivator(this, ['atom', 'textmate']) + initialize: (params) -> + {configDirPath, @devMode, safeMode, @resourcePath} = params + if configDirPath? and not safeMode + if @devMode + @packageDirPaths.push(path.join(configDirPath, "dev", "packages")) + @packageDirPaths.push(path.join(configDirPath, "packages")) + setContextMenuManager: (@contextMenuManager) -> setMenuManager: (@menuManager) -> diff --git a/src/pane-container.coffee b/src/pane-container.coffee index fc092122e..20d14389d 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -21,9 +21,11 @@ class PaneContainer extends Model @setRoot(new Pane({container: this, @config, applicationDelegate, notificationManager, deserializerManager})) @setActivePane(@getRoot()) - @monitorActivePaneItem() @monitorPaneItems() + initialize: -> + @monitorActivePaneItem() + serialize: (params) -> deserializer: 'PaneContainer' version: @serializationVersion diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index 8196d9237..f3ede7987 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -2,7 +2,7 @@ Grim = require 'grim' module.exports = ({commandRegistry, commandInstaller, config, notificationManager, project, clipboard}) -> - commandRegistry.add 'atom-workspace', + commandRegistry.addBundled 'atom-workspace', 'pane:show-next-recently-used-item': -> @getModel().getActivePane().activateNextRecentlyUsedItem() 'pane:show-previous-recently-used-item': -> @getModel().getActivePane().activatePreviousRecentlyUsedItem() 'pane:move-active-item-to-top-of-stack': -> @getModel().getActivePane().moveActiveItemToTopOfStack() @@ -79,10 +79,10 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'core:save-as': -> @getModel().saveActivePaneItemAs() if process.platform is 'darwin' - commandRegistry.add 'atom-workspace', 'window:install-shell-commands', -> + commandRegistry.addBundled 'atom-workspace', 'window:install-shell-commands', -> commandInstaller.installShellCommandsInteractively() - commandRegistry.add 'atom-pane', + commandRegistry.addBundled 'atom-pane', 'pane:save-items': -> @getModel().saveItems() 'pane:split-left': -> @getModel().splitLeft() 'pane:split-right': -> @getModel().splitRight() @@ -101,7 +101,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'pane:increase-size': -> @getModel().increaseSize() 'pane:decrease-size': -> @getModel().decreaseSize() - commandRegistry.add 'atom-text-editor', stopEventPropagation( + commandRegistry.addBundled 'atom-text-editor', stopEventPropagation( 'core:undo': -> @undo() 'core:redo': -> @redo() 'core:move-left': -> @moveLeft() @@ -142,7 +142,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:select-line': -> @selectLinesContainingCursors() ) - commandRegistry.add 'atom-text-editor', stopEventPropagationAndGroupUndo(config, + commandRegistry.addBundled 'atom-text-editor', stopEventPropagationAndGroupUndo(config, 'core:backspace': -> @backspace() 'core:delete': -> @delete() 'core:cut': -> @cutSelectedText() @@ -165,7 +165,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:copy-selection': -> @copyOnlySelectedText() ) - commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagation( + commandRegistry.addBundled 'atom-text-editor:not([mini])', stopEventPropagation( 'core:move-up': -> @moveUp() 'core:move-down': -> @moveDown() 'core:move-to-top': -> @moveToTop() @@ -203,7 +203,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:scroll-to-cursor': -> @scrollToCursorPosition() ) - commandRegistry.add 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(config, + commandRegistry.addBundled 'atom-text-editor:not([mini])', stopEventPropagationAndGroupUndo(config, 'editor:indent': -> @indent() 'editor:auto-indent': -> @autoIndentSelectedRows() 'editor:indent-selected-rows': -> @indentSelectedRows() diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 58297b2db..f035889a2 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -9,7 +9,7 @@ fs = require 'fs-plus' # An instance of this class is always available as the `atom.themes` global. module.exports = class ThemeManager - constructor: ({@packageManager, @resourcePath, @configDirPath, @safeMode, @config, @styleManager, @notificationManager, @viewRegistry}) -> + constructor: ({@packageManager, @config, @styleManager, @notificationManager, @viewRegistry}) -> @emitter = new Emitter @styleSheetDisposablesBySourcePath = {} @lessCache = null @@ -18,6 +18,8 @@ class ThemeManager @packageManager.onDidActivateInitialPackages => @onDidChangeActiveThemes => @packageManager.reloadActivePackageStyleSheets() + initialize: ({@resourcePath, @configDirPath, @safeMode}) -> + ### Section: Event Subscription ### diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 95cd45de9..5d6ce4686 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -4,10 +4,27 @@ listen = require './delegated-listener' # Handles low-level events related to the @window. module.exports = class WindowEventHandler - constructor: ({@atomEnvironment, @applicationDelegate, @window, @document}) -> + constructor: ({@atomEnvironment, @applicationDelegate}) -> @reloadRequested = false @subscriptions = new CompositeDisposable + @handleNativeKeybindings() + + initialize: (@window, @document) -> + @subscriptions.add @atomEnvironment.commands.add @window, + 'window:toggle-full-screen': @handleWindowToggleFullScreen + 'window:close': @handleWindowClose + 'window:reload': @handleWindowReload + 'window:toggle-dev-tools': @handleWindowToggleDevTools + + if process.platform in ['win32', 'linux'] + @subscriptions.add @atomEnvironment.commands.add @window, + 'window:toggle-menu-bar': @handleWindowToggleMenuBar + + @subscriptions.add @atomEnvironment.commands.add @document, + 'core:focus-next': @handleFocusNext + 'core:focus-previous': @handleFocusPrevious + @addEventListener(@window, 'beforeunload', @handleWindowBeforeunload) @addEventListener(@window, 'focus', @handleWindowFocus) @addEventListener(@window, 'blur', @handleWindowBlur) @@ -23,27 +40,11 @@ class WindowEventHandler @subscriptions.add(@applicationDelegate.onDidEnterFullScreen(@handleEnterFullScreen)) @subscriptions.add(@applicationDelegate.onDidLeaveFullScreen(@handleLeaveFullScreen)) - @subscriptions.add @atomEnvironment.commands.add @window, - 'window:toggle-full-screen': @handleWindowToggleFullScreen - 'window:close': @handleWindowClose - 'window:reload': @handleWindowReload - 'window:toggle-dev-tools': @handleWindowToggleDevTools - - if process.platform in ['win32', 'linux'] - @subscriptions.add @atomEnvironment.commands.add @window, - 'window:toggle-menu-bar': @handleWindowToggleMenuBar - - @subscriptions.add @atomEnvironment.commands.add @document, - 'core:focus-next': @handleFocusNext - 'core:focus-previous': @handleFocusPrevious - - @handleNativeKeybindings() - # Wire commands that should be handled by Chromium for elements with the # `.native-key-bindings` class. handleNativeKeybindings: -> bindCommandToAction = (command, action) => - @subscriptions.add @atomEnvironment.commands.add '.native-key-bindings', command, (event) => + @subscriptions.add @atomEnvironment.commands.addBundled '.native-key-bindings', command, (event) => @applicationDelegate.getCurrentWindow().webContents[action]() bindCommandToAction('core:copy', 'copy') diff --git a/src/workspace.js b/src/workspace.js index f411cf823..de558dfa9 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -30,6 +30,7 @@ module.exports = class Workspace extends Model { this.updateWindowTitle = this.updateWindowTitle.bind(this) this.updateDocumentEdited = this.updateDocumentEdited.bind(this) this.didDestroyPaneItem = this.didDestroyPaneItem.bind(this) + this.didChangeActivePaneItem = this.didChangeActivePaneItem.bind(this) this.packageManager = params.packageManager this.config = params.config @@ -76,6 +77,11 @@ module.exports = class Workspace extends Model { this.subscribeToEvents() } + initialize () { + this.paneContainer.initialize() + this.didChangeActivePaneItem() + } + reset (packageManager) { this.packageManager = packageManager this.emitter.dispose() @@ -176,46 +182,44 @@ module.exports = class Workspace extends Model { } subscribeToActiveItem () { + this.project.onDidChangePaths(this.updateWindowTitle) + this.onDidChangeActivePaneItem(this.didChangeActivePaneItem) + } + + didChangeActivePaneItem (item) { this.updateWindowTitle() this.updateDocumentEdited() - this.project.onDidChangePaths(this.updateWindowTitle) + if (this.activeItemSubscriptions != null) { + this.activeItemSubscriptions.dispose() + } + this.activeItemSubscriptions = new CompositeDisposable() - this.observeActivePaneItem(item => { - this.updateWindowTitle() - this.updateDocumentEdited() + let modifiedSubscription, titleSubscription - if (this.activeItemSubscriptions != null) { - this.activeItemSubscriptions.dispose() + if (item != null && typeof item.onDidChangeTitle === 'function') { + titleSubscription = item.onDidChangeTitle(this.updateWindowTitle) + } else if (item != null && typeof item.on === 'function') { + titleSubscription = item.on('title-changed', this.updateWindowTitle) + if (titleSubscription == null || typeof titleSubscription.dispose !== 'function') { + titleSubscription = new Disposable(() => { + item.off('title-changed', this.updateWindowTitle) + }) } - this.activeItemSubscriptions = new CompositeDisposable() + } - let modifiedSubscription, titleSubscription - - if (item != null && typeof item.onDidChangeTitle === 'function') { - titleSubscription = item.onDidChangeTitle(this.updateWindowTitle) - } else if (item != null && typeof item.on === 'function') { - titleSubscription = item.on('title-changed', this.updateWindowTitle) - if (titleSubscription == null || typeof titleSubscription.dispose !== 'function') { - titleSubscription = new Disposable(() => { - item.off('title-changed', this.updateWindowTitle) - }) - } + if (item != null && typeof item.onDidChangeModified === 'function') { + modifiedSubscription = item.onDidChangeModified(this.updateDocumentEdited) + } else if (item != null && typeof item.on === 'function') { + modifiedSubscription = item.on('modified-status-changed', this.updateDocumentEdited) + if (modifiedSubscription == null || typeof modifiedSubscription.dispose !== 'function') { + modifiedSubscription = new Disposable(() => { + item.off('modified-status-changed', this.updateDocumentEdited) + }) } + } - if (item != null && typeof item.onDidChangeModified === 'function') { - modifiedSubscription = item.onDidChangeModified(this.updateDocumentEdited) - } else if (item != null && typeof item.on === 'function') { - modifiedSubscription = item.on('modified-status-changed', this.updateDocumentEdited) - if (modifiedSubscription == null || typeof modifiedSubscription.dispose !== 'function') { - modifiedSubscription = new Disposable(() => { - item.off('modified-status-changed', this.updateDocumentEdited) - }) - } - } - - if (titleSubscription != null) { this.activeItemSubscriptions.add(titleSubscription) } - if (modifiedSubscription != null) { this.activeItemSubscriptions.add(modifiedSubscription) } - }) + if (titleSubscription != null) { this.activeItemSubscriptions.add(titleSubscription) } + if (modifiedSubscription != null) { this.activeItemSubscriptions.add(modifiedSubscription) } } subscribeToAddedItems () { From 9d123afb81b3f3cfca200e7150e2a8a1ac795f5f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2017 12:39:04 +0100 Subject: [PATCH 05/17] Move StateStore into the snapshot --- src/atom-environment.coffee | 7 +++---- src/history-manager.js | 7 ++----- src/state-store.js | 41 ++++++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 2c5398d64..44fdf2e8c 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -143,6 +143,8 @@ class AtomEnvironment extends Model @views = new ViewRegistry(this) @notifications = new NotificationManager + @stateStore = new StateStore('AtomEnvironments', 1) + @config = new Config({notificationManager: @notifications, @enablePersistence}) @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} @@ -192,7 +194,7 @@ class AtomEnvironment extends Model @windowEventHandler = new WindowEventHandler({atomEnvironment: this, @applicationDelegate}) - @history = new HistoryManager({@project, @commands}) + @history = new HistoryManager({@project, @commands, @stateStore}) # Keep instances of HistoryManager in sync @disposables.add @history.onDidChangeProjects (e) => @applicationDelegate.didChangeHistoryManager() unless e.reloaded @@ -201,8 +203,6 @@ class AtomEnvironment extends Model {@window, @document, @blobStore, @configDirPath, onlyLoadBaseStyleSheets} = params {devMode, safeMode, resourcePath, clearWindowState} = @getLoadSettings() - @stateStore = new StateStore('AtomEnvironments', 1) - if clearWindowState @getStorageFolder().clear() @stateStore.clear() @@ -255,7 +255,6 @@ class AtomEnvironment extends Model @observeAutoHideMenuBar() - @history.initialize(@stateStore) @disposables.add @applicationDelegate.onDidChangeHistoryManager(=> @history.loadState()) attachSaveStateListeners: -> diff --git a/src/history-manager.js b/src/history-manager.js index ff3fbc218..9f60ac516 100644 --- a/src/history-manager.js +++ b/src/history-manager.js @@ -8,7 +8,8 @@ import {Emitter, CompositeDisposable} from 'event-kit' // // The project history is used to enable the 'Reopen Project' menu. export class HistoryManager { - constructor ({project, commands}) { + constructor ({project, commands, stateStore}) { + this.stateStore = stateStore this.emitter = new Emitter() this.projects = [] this.disposables = new CompositeDisposable() @@ -16,10 +17,6 @@ export class HistoryManager { this.disposables.add(project.onDidChangePaths((projectPaths) => this.addProject(projectPaths))) } - initialize (stateStore) { - this.stateStore = stateStore - } - destroy () { this.disposables.dispose() } diff --git a/src/state-store.js b/src/state-store.js index b192d8b04..e16857580 100644 --- a/src/state-store.js +++ b/src/state-store.js @@ -4,22 +4,31 @@ module.exports = class StateStore { constructor (databaseName, version) { this.connected = false - this.dbPromise = new Promise((resolve) => { - let dbOpenRequest = indexedDB.open(databaseName, version) - dbOpenRequest.onupgradeneeded = (event) => { - let db = event.target.result - db.createObjectStore('states') - } - dbOpenRequest.onsuccess = () => { - this.connected = true - resolve(dbOpenRequest.result) - } - dbOpenRequest.onerror = (error) => { - console.error('Could not connect to indexedDB', error) - this.connected = false - resolve(null) - } - }) + this.databaseName = databaseName + this.version = version + } + + get dbPromise () { + if (!this._dbPromise) { + this._dbPromise = new Promise((resolve) => { + const dbOpenRequest = indexedDB.open(this.databaseName, this.version) + dbOpenRequest.onupgradeneeded = (event) => { + let db = event.target.result + db.createObjectStore('states') + } + dbOpenRequest.onsuccess = () => { + this.connected = true + resolve(dbOpenRequest.result) + } + dbOpenRequest.onerror = (error) => { + console.error('Could not connect to indexedDB', error) + this.connected = false + resolve(null) + } + }) + } + + return this._dbPromise } isConnected () { From 47391110e84ed8292be3a58974411416be62903e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2017 15:28:19 +0100 Subject: [PATCH 06/17] Set config schema entirely in the snapshot --- src/atom-environment.coffee | 17 +++++------------ src/config.coffee | 4 +++- src/main-process/atom-application.coffee | 10 ++++++++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 44fdf2e8c..8769db433 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -209,18 +209,12 @@ class AtomEnvironment extends Model @views.initialize() - @config.initialize({@configDirPath, resourcePath}) - @projectHomeSchema = { - type: 'object', - properties: { - projectHome: { - type: 'string', - default: path.join(fs.getHomeDirectory(), 'github'), - description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' - } - } + ConfigSchema.projectHome = { + type: 'string', + default: path.join(fs.getHomeDirectory(), 'github'), + description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' } - @config.setSchema('core', @projectHomeSchema) + @config.initialize({@configDirPath, resourcePath, projectHomeSchema: ConfigSchema.projectHome}) @keymaps.configDirPath = @configDirPath @keymaps.resourcePath = resourcePath @@ -320,7 +314,6 @@ class AtomEnvironment extends Model @config.clear() @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} - @config.setSchema('core', @projectHomeSchema) @keymaps.clear() @keymaps.loadBundledKeymaps() diff --git a/src/config.coffee b/src/config.coffee index 575e95a72..46f3c8629 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -401,11 +401,13 @@ class Config constructor: ({@notificationManager, @enablePersistence}={}) -> @clear() - initialize: ({@configDirPath, @resourcePath}) -> + initialize: ({@configDirPath, @resourcePath, projectHomeSchema}) -> if @enablePersistence? @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) @configFilePath ?= path.join(@configDirPath, 'config.cson') + @schema.properties.core.properties.projectHome = projectHomeSchema + clear: -> @emitter = new Emitter @schema = diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 73eb5a134..4904dbd70 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -17,6 +17,7 @@ url = require 'url' _ = require 'underscore-plus' FindParentDir = null Resolve = null +ConfigSchema = require '../config-schema' LocationSuffixRegExp = /(:\d+)(:\d+)?$/ @@ -69,8 +70,13 @@ class AtomApplication @windows = [] @config = new Config({enablePersistence: true}) - @config.initialize({configDirPath: process.env.ATOM_HOME, @resourcePath}) - @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))} + @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} + ConfigSchema.projectHome = { + type: 'string', + default: path.join(fs.getHomeDirectory(), 'github'), + description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' + } + @config.initialize({configDirPath: process.env.ATOM_HOME, @resourcePath, projectHomeSchema: ConfigSchema.projectHome}) @config.load() @fileRecoveryService = new FileRecoveryService(path.join(process.env.ATOM_HOME, "recovery")) @storageFolder = new StorageFolder(process.env.ATOM_HOME) From fce10242dc906bf7079c8b5ac3a57f342bbd34f5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2017 18:10:54 +0100 Subject: [PATCH 07/17] Don't load base style sheets in AtomEnvironment As it will happen again right after package activation. Signed-off-by: Nathan Sobo --- src/atom-environment.coffee | 1 - src/theme-manager.coffee | 12 +++++------- src/workspace-element.coffee | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8769db433..958d1d973 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -233,7 +233,6 @@ class AtomEnvironment extends Model @config.load() - @themes.loadBaseStylesheets() @initialStyleElements = @styles.getSnapshot() @themes.initialLoadComplete = true if onlyLoadBaseStyleSheets @setBodyPlatformClass() diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index f035889a2..9e7c20380 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -126,10 +126,10 @@ class ThemeManager # # Returns a {Disposable} on which `.dispose()` can be called to remove the # required stylesheet. - requireStylesheet: (stylesheetPath) -> + requireStylesheet: (stylesheetPath, priority) -> if fullPath = @resolveStylesheet(stylesheetPath) content = @loadStylesheet(fullPath) - @applyStylesheet(fullPath, content) + @applyStylesheet(fullPath, content, priority) else throw new Error("Could not find a file at path '#{stylesheetPath}'") @@ -175,9 +175,7 @@ class ThemeManager @reloadBaseStylesheets() reloadBaseStylesheets: -> - @requireStylesheet('../static/atom') - if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less']) - @requireStylesheet(nativeStylesheetPath) + @requireStylesheet('../static/atom', -2) stylesheetElementForId: (id) -> escapedId = id.replace(/\\/g, '\\\\') @@ -231,8 +229,8 @@ class ThemeManager removeStylesheet: (stylesheetPath) -> @styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose() - applyStylesheet: (path, text) -> - @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(text, sourcePath: path) + applyStylesheet: (path, text, priority) -> + @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(text, {priority, sourcePath: path}) activateThemes: -> new Promise (resolve) => diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index 6defe33da..cee3c09f3 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -51,7 +51,7 @@ class WorkspaceElement extends HTMLElement line-height: #{@config.get('editor.lineHeight')}; } """ - @styles.addStyleSheet(styleSheetSource, sourcePath: 'global-text-editor-styles') + @styles.addStyleSheet(styleSheetSource, {sourcePath: 'global-text-editor-styles', priority: -1}) @views.performDocumentPoll() initialize: (@model, {@views, @workspace, @project, @config, @styles}) -> From 4c6805bf7647efdf2d5d607e0b6dea1f98e9ed73 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2017 14:23:58 +0100 Subject: [PATCH 08/17] Store style sheet sources as auxiliary data in the snapshot --- script/config.js | 3 ++- script/lib/generate-startup-snapshot.js | 1 + script/lib/prebuild-less-cache.js | 23 +++++++++++++++++++++++ script/package.json | 2 +- script/tdd | 2 ++ src/less-compile-cache.coffee | 3 ++- src/theme-manager.coffee | 11 +++++++++-- 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/script/config.js b/script/config.js index 293af1136..2b9641fe2 100644 --- a/script/config.js +++ b/script/config.js @@ -26,7 +26,8 @@ module.exports = { repositoryRootPath, apmRootPath, scriptRootPath, buildOutputPath, docsOutputPath, intermediateAppPath, symbolsPath, electronDownloadPath, atomHomeDirPath, homeDirPath, - getApmBinPath, getNpmBinPath + getApmBinPath, getNpmBinPath, + snapshotAuxiliaryData: {} } function getChannel () { diff --git a/script/lib/generate-startup-snapshot.js b/script/lib/generate-startup-snapshot.js index be3f06839..3e1c7c0d2 100644 --- a/script/lib/generate-startup-snapshot.js +++ b/script/lib/generate-startup-snapshot.js @@ -15,6 +15,7 @@ module.exports = function (packagedAppPath) { baseDirPath, mainPath: path.resolve(baseDirPath, '..', 'src', 'initialize-application-window.js'), cachePath: path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'), + auxiliaryData: CONFIG.snapshotAuxiliaryData, shouldExcludeModule: (modulePath) => { if (processedFiles > 0) { process.stdout.write('\r') diff --git a/script/lib/prebuild-less-cache.js b/script/lib/prebuild-less-cache.js index 1a1432fc9..bd1cf7338 100644 --- a/script/lib/prebuild-less-cache.js +++ b/script/lib/prebuild-less-cache.js @@ -28,6 +28,14 @@ module.exports = function () { } } + CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath = {} + function saveIntoSnapshotAuxiliaryData (absoluteFilePath, contents) { + const relativeFilePath = path.relative(CONFIG.intermediateAppPath, absoluteFilePath) + if (!CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath.hasOwnProperty(relativeFilePath)) { + CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[relativeFilePath] = contents + } + } + // Warm cache for every combination of the default UI and syntax themes, // because themes assign variables which may be used in any style sheet. for (let uiTheme of uiThemes) { @@ -52,6 +60,7 @@ module.exports = function () { lessSource = FALLBACK_VARIABLE_IMPORTS + lessSource } lessCache.cssForFile(lessFilePath, lessSource) + saveIntoSnapshotAuxiliaryData(lessFilePath, lessSource) } // Cache all styles in static; don't append variable imports @@ -69,10 +78,24 @@ module.exports = function () { // Cache styles for this UI theme const uiThemeMainPath = path.join(CONFIG.intermediateAppPath, 'node_modules', uiTheme, 'index.less') cacheCompiledCSS(uiThemeMainPath, true) + for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', uiTheme, '**', '*.less'))) { + if (lessFilePath !== uiThemeMainPath) { + saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8')) + } + } // Cache styles for this syntax theme const syntaxThemeMainPath = path.join(CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, 'index.less') cacheCompiledCSS(syntaxThemeMainPath, true) + for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, '**', '*.less'))) { + if (lessFilePath !== syntaxThemeMainPath) { + saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8')) + } + } } } + + for (let lessFilePath of glob.sync(path.join(CONFIG.intermediateAppPath, 'node_modules', 'atom-ui', '**', '*.less'))) { + saveIntoSnapshotAuxiliaryData(lessFilePath, fs.readFileSync(lessFilePath, 'utf8')) + } } diff --git a/script/package.json b/script/package.json index c8106cc4d..f149189b0 100644 --- a/script/package.json +++ b/script/package.json @@ -8,7 +8,7 @@ "csslint": "1.0.2", "donna": "1.0.13", "electron-chromedriver": "~1.3", - "electron-link": "0.0.10", + "electron-link": "0.0.12", "electron-mksnapshot": "~1.3", "electron-packager": "7.3.0", "electron-winstaller": "2.5.1", diff --git a/script/tdd b/script/tdd index db78ef3e4..9c95be288 100755 --- a/script/tdd +++ b/script/tdd @@ -25,6 +25,7 @@ const generateMetadata = require('./lib/generate-metadata') const generateModuleCache = require('./lib/generate-module-cache') const generateStartupSnapshot = require('./lib/generate-startup-snapshot') const packageApplication = require('./lib/package-application') +const prebuildLessCache = require('./lib/prebuild-less-cache') const transpileBabelPaths = require('./lib/fast-transpile-babel-paths') const transpileCoffeeScriptPaths = require('./lib/fast-transpile-coffee-script-paths') @@ -39,4 +40,5 @@ transpileBabelPaths() transpileCoffeeScriptPaths() generateModuleCache() generateMetadata() +prebuildLessCache() packageApplication().then(generateStartupSnapshot) diff --git a/src/less-compile-cache.coffee b/src/less-compile-cache.coffee index c70f312ee..849ad0d55 100644 --- a/src/less-compile-cache.coffee +++ b/src/less-compile-cache.coffee @@ -6,7 +6,7 @@ module.exports = class LessCompileCache @cacheDir: path.join(process.env.ATOM_HOME, 'compile-cache', 'less') - constructor: ({resourcePath, importPaths}) -> + constructor: ({resourcePath, importPaths, lessSourcesByRelativeFilePath}) -> @lessSearchPaths = [ path.join(resourcePath, 'static', 'variables') path.join(resourcePath, 'static') @@ -21,6 +21,7 @@ class LessCompileCache cacheDir: @constructor.cacheDir importPaths: importPaths resourcePath: resourcePath + lessSourcesByRelativeFilePath: lessSourcesByRelativeFilePath fallbackDir: path.join(resourcePath, 'less-compile-cache') setImportPaths: (importPaths=[]) -> diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 9e7c20380..6163ad3fc 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -18,6 +18,12 @@ class ThemeManager @packageManager.onDidActivateInitialPackages => @onDidChangeActiveThemes => @packageManager.reloadActivePackageStyleSheets() + @lessSourcesByRelativeFilePath = null + if typeof snapshotAuxiliaryData is 'undefined' + @lessSourcesByRelativeFilePath = {} + else + @lessSourcesByRelativeFilePath = snapshotAuxiliaryData.lessSourcesByRelativeFilePath + initialize: ({@resourcePath, @configDirPath, @safeMode}) -> ### @@ -196,7 +202,7 @@ class ThemeManager loadLessStylesheet: (lessStylesheetPath, importFallbackVariables=false) -> unless @lessCache? LessCompileCache = require './less-compile-cache' - @lessCache = new LessCompileCache({@resourcePath, importPaths: @getImportPaths()}) + @lessCache = new LessCompileCache({@resourcePath, @lessSourcesByRelativeFilePath, importPaths: @getImportPaths()}) try if importFallbackVariables @@ -204,7 +210,8 @@ class ThemeManager @import "variables/ui-variables"; @import "variables/syntax-variables"; """ - less = fs.readFileSync(lessStylesheetPath, 'utf8') + relativeFilePath = path.relative(@resourcePath, lessStylesheetPath) + less = @lessSourcesByRelativeFilePath[relativeFilePath] ? fs.readFileSync(lessStylesheetPath, 'utf8') @lessCache.cssForFile(lessStylesheetPath, [baseVarImports, less].join('\n')) else @lessCache.read(lessStylesheetPath) From 19d07172a7a59364e4c120f8eed8673a765ac0bd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2017 16:15:47 +0100 Subject: [PATCH 09/17] Don't transform deprecated selectors for bundled packages --- src/package.coffee | 12 +++++++++- src/style-manager.js | 53 ++++++++++++++++++++++++++------------------ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/package.coffee b/src/package.coffee index ed0f7aa87..4ea09e57c 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -204,7 +204,17 @@ class Package else context = undefined - @stylesheetDisposables.add(@styleManager.addStyleSheet(source, {sourcePath, priority, context})) + @stylesheetDisposables.add( + @styleManager.addStyleSheet( + source, + { + sourcePath, + priority, + context, + skipDeprecatedSelectorsTransformation: @bundledPackage + } + ) + ) @stylesheetsActivated = true activateResources: -> diff --git a/src/style-manager.js b/src/style-manager.js index 718c1ee74..6ffc8de7c 100644 --- a/src/style-manager.js +++ b/src/style-manager.js @@ -137,29 +137,17 @@ module.exports = class StyleManager { } } - let transformed - if (this.cacheDirPath != null) { - const hash = crypto.createHash('sha1') - if (params.context != null) { - hash.update(params.context) - } - hash.update(source) - const cacheFilePath = path.join(this.cacheDirPath, hash.digest('hex')) - try { - transformed = JSON.parse(fs.readFileSync(cacheFilePath)) - } catch (e) { - transformed = transformDeprecatedShadowDOMSelectors(source, params.context) - fs.writeFileSync(cacheFilePath, JSON.stringify(transformed)) - } + if (params.skipDeprecatedSelectorsTransformation) { + styleElement.textContent = source } else { - transformed = transformDeprecatedShadowDOMSelectors(source, params.context) + const transformed = this.upgradeDeprecatedSelectorsForStyleSheet(source, params.context) + styleElement.textContent = transformed.source + if (transformed.deprecationMessage) { + this.deprecationsBySourcePath[params.sourcePath] = {message: transformed.deprecationMessage} + this.emitter.emit('did-update-deprecations') + } } - styleElement.textContent = transformed.source - if (transformed.deprecationMessage) { - this.deprecationsBySourcePath[params.sourcePath] = {message: transformed.deprecationMessage} - this.emitter.emit('did-update-deprecations') - } if (updated) { this.emitter.emit('did-update-style-element', styleElement) } else { @@ -171,9 +159,10 @@ module.exports = class StyleManager { addStyleElement (styleElement) { let insertIndex = this.styleElements.length if (styleElement.priority != null) { - for (let [index, existingElement] of this.styleElements.entries()) { + for (let i = 0; i < this.styleElements.length; i++) { + const existingElement = this.styleElements[i] if (existingElement.priority > styleElement.priority) { - insertIndex = index + insertIndex = i break } } @@ -197,6 +186,26 @@ module.exports = class StyleManager { } } + upgradeDeprecatedSelectorsForStyleSheet (styleSheet, context) { + if (this.cacheDirPath != null) { + const hash = crypto.createHash('sha1') + if (context != null) { + hash.update(context) + } + hash.update(styleSheet) + const cacheFilePath = path.join(this.cacheDirPath, hash.digest('hex')) + try { + return JSON.parse(fs.readFileSync(cacheFilePath)) + } catch (e) { + const transformed = transformDeprecatedShadowDOMSelectors(styleSheet, context) + fs.writeFileSync(cacheFilePath, JSON.stringify(transformed)) + return transformed + } + } else { + return transformDeprecatedShadowDOMSelectors(styleSheet, context) + } + } + getDeprecations () { return this.deprecationsBySourcePath } From abeedfe0c5e64acd4d06838a42672da0e6b4b668 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 13 Mar 2017 19:05:14 +0100 Subject: [PATCH 10/17] Skip deprecated selectors upgrade for base style sheets too --- src/theme-manager.coffee | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 6163ad3fc..8238aa06e 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -132,10 +132,10 @@ class ThemeManager # # Returns a {Disposable} on which `.dispose()` can be called to remove the # required stylesheet. - requireStylesheet: (stylesheetPath, priority) -> + requireStylesheet: (stylesheetPath, priority, skipDeprecatedSelectorsTransformation) -> if fullPath = @resolveStylesheet(stylesheetPath) content = @loadStylesheet(fullPath) - @applyStylesheet(fullPath, content, priority) + @applyStylesheet(fullPath, content, priority, skipDeprecatedSelectorsTransformation) else throw new Error("Could not find a file at path '#{stylesheetPath}'") @@ -181,7 +181,7 @@ class ThemeManager @reloadBaseStylesheets() reloadBaseStylesheets: -> - @requireStylesheet('../static/atom', -2) + @requireStylesheet('../static/atom', -2, true) stylesheetElementForId: (id) -> escapedId = id.replace(/\\/g, '\\\\') @@ -236,8 +236,15 @@ class ThemeManager removeStylesheet: (stylesheetPath) -> @styleSheetDisposablesBySourcePath[stylesheetPath]?.dispose() - applyStylesheet: (path, text, priority) -> - @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet(text, {priority, sourcePath: path}) + applyStylesheet: (path, text, priority, skipDeprecatedSelectorsTransformation) -> + @styleSheetDisposablesBySourcePath[path] = @styleManager.addStyleSheet( + text, + { + priority, + skipDeprecatedSelectorsTransformation, + sourcePath: path + } + ) activateThemes: -> new Promise (resolve) => From dc76e2e1445a41cfc8898642227b94eef683c239 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Mar 2017 08:59:21 +0100 Subject: [PATCH 11/17] Load base style sheets in AtomEnvironment again --- src/atom-environment.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 3cc8debbf..13f8ae3c9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -240,6 +240,7 @@ class AtomEnvironment extends Model @config.load() + @themes.loadBaseStylesheets() @initialStyleElements = @styles.getSnapshot() @themes.initialLoadComplete = true if onlyLoadBaseStyleSheets @setBodyPlatformClass() From 684d8bd42cc9d52a66c1b6e72dd5959803075870 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Mar 2017 13:35:28 +0100 Subject: [PATCH 12/17] Provide importedFilePathsByRelativeImportPath to LessCache --- script/lib/prebuild-less-cache.js | 22 ++++++++++++++++++++-- script/package.json | 1 + src/less-compile-cache.coffee | 18 ++++++++++-------- src/theme-manager.coffee | 20 +++++++++++++++++--- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/script/lib/prebuild-less-cache.js b/script/lib/prebuild-less-cache.js index bd1cf7338..ee2dee6ec 100644 --- a/script/lib/prebuild-less-cache.js +++ b/script/lib/prebuild-less-cache.js @@ -1,6 +1,7 @@ 'use strict' const fs = require('fs') +const klawSync = require('klaw-sync') const glob = require('glob') const path = require('path') const LessCache = require('less-cache') @@ -29,13 +30,17 @@ module.exports = function () { } CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath = {} - function saveIntoSnapshotAuxiliaryData (absoluteFilePath, contents) { + function saveIntoSnapshotAuxiliaryData (absoluteFilePath, content) { const relativeFilePath = path.relative(CONFIG.intermediateAppPath, absoluteFilePath) if (!CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath.hasOwnProperty(relativeFilePath)) { - CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[relativeFilePath] = contents + CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[relativeFilePath] = { + content: content, + digest: LessCache.digestForContent(content) + } } } + CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath = {} // Warm cache for every combination of the default UI and syntax themes, // because themes assign variables which may be used in any style sheet. for (let uiTheme of uiThemes) { @@ -54,6 +59,19 @@ module.exports = function () { ] }) + // Store file paths located at the import paths so that we can avoid scanning them at runtime. + for (const absoluteImportPath of lessCache.getImportPaths()) { + const relativeImportPath = path.relative(CONFIG.intermediateAppPath, absoluteImportPath) + if (!CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath.hasOwnProperty(relativeImportPath)) { + CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[relativeImportPath] = [] + for (const importedFile of klawSync(absoluteImportPath, {nodir: true})) { + CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[relativeImportPath].push( + path.relative(CONFIG.intermediateAppPath, importedFile.path) + ) + } + } + } + function cacheCompiledCSS(lessFilePath, importFallbackVariables) { let lessSource = fs.readFileSync(lessFilePath, 'utf8') if (importFallbackVariables) { diff --git a/script/package.json b/script/package.json index 9441213ac..f52bd5df5 100644 --- a/script/package.json +++ b/script/package.json @@ -15,6 +15,7 @@ "fs-extra": "0.30.0", "glob": "7.0.3", "joanna": "0.0.8", + "klaw-sync": "^1.1.2", "legal-eagle": "0.14.0", "lodash.template": "4.4.0", "minidump": "0.9.0", diff --git a/src/less-compile-cache.coffee b/src/less-compile-cache.coffee index 849ad0d55..f74c7bd45 100644 --- a/src/less-compile-cache.coffee +++ b/src/less-compile-cache.coffee @@ -6,7 +6,7 @@ module.exports = class LessCompileCache @cacheDir: path.join(process.env.ATOM_HOME, 'compile-cache', 'less') - constructor: ({resourcePath, importPaths, lessSourcesByRelativeFilePath}) -> + constructor: ({resourcePath, importPaths, lessSourcesByRelativeFilePath, importedFilePathsByRelativeImportPath}) -> @lessSearchPaths = [ path.join(resourcePath, 'static', 'variables') path.join(resourcePath, 'static') @@ -17,12 +17,14 @@ class LessCompileCache else importPaths = @lessSearchPaths - @cache = new LessCache - cacheDir: @constructor.cacheDir - importPaths: importPaths - resourcePath: resourcePath - lessSourcesByRelativeFilePath: lessSourcesByRelativeFilePath + @cache = new LessCache({ + importPaths, + resourcePath, + lessSourcesByRelativeFilePath, + importedFilePathsByRelativeImportPath, + cacheDir: @constructor.cacheDir, fallbackDir: path.join(resourcePath, 'less-compile-cache') + }) setImportPaths: (importPaths=[]) -> @cache.setImportPaths(importPaths.concat(@lessSearchPaths)) @@ -30,5 +32,5 @@ class LessCompileCache read: (stylesheetPath) -> @cache.readFileSync(stylesheetPath) - cssForFile: (stylesheetPath, lessContent) -> - @cache.cssForFile(stylesheetPath, lessContent) + cssForFile: (stylesheetPath, lessContent, digest) -> + @cache.cssForFile(stylesheetPath, lessContent, digest) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 8238aa06e..8c673e01f 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -21,8 +21,10 @@ class ThemeManager @lessSourcesByRelativeFilePath = null if typeof snapshotAuxiliaryData is 'undefined' @lessSourcesByRelativeFilePath = {} + @importedFilePathsByRelativeImportPath = {} else @lessSourcesByRelativeFilePath = snapshotAuxiliaryData.lessSourcesByRelativeFilePath + @importedFilePathsByRelativeImportPath = snapshotAuxiliaryData.importedFilePathsByRelativeImportPath initialize: ({@resourcePath, @configDirPath, @safeMode}) -> @@ -202,7 +204,12 @@ class ThemeManager loadLessStylesheet: (lessStylesheetPath, importFallbackVariables=false) -> unless @lessCache? LessCompileCache = require './less-compile-cache' - @lessCache = new LessCompileCache({@resourcePath, @lessSourcesByRelativeFilePath, importPaths: @getImportPaths()}) + @lessCache = new LessCompileCache({ + @resourcePath, + @lessSourcesByRelativeFilePath, + @importedFilePathsByRelativeImportPath, + importPaths: @getImportPaths() + }) try if importFallbackVariables @@ -211,8 +218,15 @@ class ThemeManager @import "variables/syntax-variables"; """ relativeFilePath = path.relative(@resourcePath, lessStylesheetPath) - less = @lessSourcesByRelativeFilePath[relativeFilePath] ? fs.readFileSync(lessStylesheetPath, 'utf8') - @lessCache.cssForFile(lessStylesheetPath, [baseVarImports, less].join('\n')) + lessSource = @lessSourcesByRelativeFilePath[relativeFilePath] + if lessSource? + content = lessSource.content + digest = lessSource.digest + else + content = baseVarImports + '\n' + fs.readFileSync(lessStylesheetPath, 'utf8') + digest = null + + @lessCache.cssForFile(lessStylesheetPath, content, digest) else @lessCache.read(lessStylesheetPath) catch error From b2ef8abf0a0c6de6dc581f8a494888ddbc131c89 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Mar 2017 13:50:22 +0100 Subject: [PATCH 13/17] Don't use cached less sources and imported files in dev mode --- src/atom-environment.coffee | 2 +- src/less-compile-cache.coffee | 6 +++--- src/theme-manager.coffee | 20 +++++++++----------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 13f8ae3c9..bb43178c9 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -232,7 +232,7 @@ class AtomEnvironment extends Model @styles.initialize({@configDirPath}) @packages.initialize({devMode, @configDirPath, resourcePath, safeMode}) - @themes.initialize({@configDirPath, resourcePath, safeMode}) + @themes.initialize({@configDirPath, resourcePath, safeMode, devMode}) @commandInstaller.initialize(@getVersion()) @workspace.initialize() diff --git a/src/less-compile-cache.coffee b/src/less-compile-cache.coffee index f74c7bd45..84fd92247 100644 --- a/src/less-compile-cache.coffee +++ b/src/less-compile-cache.coffee @@ -4,9 +4,9 @@ LessCache = require 'less-cache' # {LessCache} wrapper used by {ThemeManager} to read stylesheets. module.exports = class LessCompileCache - @cacheDir: path.join(process.env.ATOM_HOME, 'compile-cache', 'less') - constructor: ({resourcePath, importPaths, lessSourcesByRelativeFilePath, importedFilePathsByRelativeImportPath}) -> + cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache', 'less') + @lessSearchPaths = [ path.join(resourcePath, 'static', 'variables') path.join(resourcePath, 'static') @@ -22,7 +22,7 @@ class LessCompileCache resourcePath, lessSourcesByRelativeFilePath, importedFilePathsByRelativeImportPath, - cacheDir: @constructor.cacheDir, + cacheDir, fallbackDir: path.join(resourcePath, 'less-compile-cache') }) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 8c673e01f..0f019dbf0 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -3,6 +3,7 @@ _ = require 'underscore-plus' {Emitter, CompositeDisposable} = require 'event-kit' {File} = require 'pathwatcher' fs = require 'fs-plus' +LessCompileCache = require './less-compile-cache' # Extended: Handles loading and activating available themes. # @@ -18,16 +19,15 @@ class ThemeManager @packageManager.onDidActivateInitialPackages => @onDidChangeActiveThemes => @packageManager.reloadActivePackageStyleSheets() + initialize: ({@resourcePath, @configDirPath, @safeMode, devMode}) -> @lessSourcesByRelativeFilePath = null - if typeof snapshotAuxiliaryData is 'undefined' + if devMode or typeof snapshotAuxiliaryData is 'undefined' @lessSourcesByRelativeFilePath = {} @importedFilePathsByRelativeImportPath = {} else @lessSourcesByRelativeFilePath = snapshotAuxiliaryData.lessSourcesByRelativeFilePath @importedFilePathsByRelativeImportPath = snapshotAuxiliaryData.importedFilePathsByRelativeImportPath - initialize: ({@resourcePath, @configDirPath, @safeMode}) -> - ### Section: Event Subscription ### @@ -202,14 +202,12 @@ class ThemeManager fs.readFileSync(stylesheetPath, 'utf8') loadLessStylesheet: (lessStylesheetPath, importFallbackVariables=false) -> - unless @lessCache? - LessCompileCache = require './less-compile-cache' - @lessCache = new LessCompileCache({ - @resourcePath, - @lessSourcesByRelativeFilePath, - @importedFilePathsByRelativeImportPath, - importPaths: @getImportPaths() - }) + @lessCache ?= new LessCompileCache({ + @resourcePath, + @lessSourcesByRelativeFilePath, + @importedFilePathsByRelativeImportPath, + importPaths: @getImportPaths() + }) try if importFallbackVariables From a1da6c9196ed8886c33e0314924d2cc94c35effe Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Mar 2017 14:27:07 +0100 Subject: [PATCH 14/17] Resolve style sheets paths during `script/build` for bundled packages --- script/lib/generate-metadata.js | 22 +++++++++++++++++++++- src/package.coffee | 20 ++++++++++++-------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/script/lib/generate-metadata.js b/script/lib/generate-metadata.js index 8267b1cee..bd6872f3a 100644 --- a/script/lib/generate-metadata.js +++ b/script/lib/generate-metadata.js @@ -2,7 +2,7 @@ const CSON = require('season') const deprecatedPackagesMetadata = require('../deprecated-packages') -const fs = require('fs-extra') +const fs = require('fs-plus') const normalizePackageData = require('normalize-package-data') const path = require('path') const semver = require('semver') @@ -76,6 +76,26 @@ function buildBundledPackagesMetadata () { } } + const packageStyleSheetsPath = path.join(packagePath, 'styles') + let styleSheets = null + if (packageMetadata.mainStyleSheet) { + styleSheets = [fs.resolve(packagePath, packageMetadata.mainStyleSheet)] + } else if (packageMetadata.styleSheets) { + styleSheets = packageMetadata.styleSheets.map((name) => ( + fs.resolve(packageStyleSheetsPath, name, ['css', 'less', '']) + )) + } else { + const indexStylesheet = fs.resolve(packagePath, 'index', ['css', 'less']) + if (indexStylesheet) { + styleSheets = [indexStylesheet] + } else { + styleSheets = fs.listSync(packageStyleSheetsPath, ['css', 'less']) + } + } + + packageNewMetadata.styleSheetsPaths = + styleSheets.map(styleSheetPath => path.relative(packagePath, styleSheetPath)) + packages[packageMetadata.name] = packageNewMetadata if (packageModuleCache.extensions) { for (let extension of Object.keys(packageModuleCache.extensions)) { diff --git a/src/package.coffee b/src/package.coffee index 4ea09e57c..21e161a26 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -358,15 +358,19 @@ class Package path.join(@path, 'styles') getStylesheetPaths: -> - stylesheetDirPath = @getStylesheetsPath() - if @metadata.mainStyleSheet - [fs.resolve(@path, @metadata.mainStyleSheet)] - else if @metadata.styleSheets - @metadata.styleSheets.map (name) -> fs.resolve(stylesheetDirPath, name, ['css', 'less', '']) - else if indexStylesheet = fs.resolve(@path, 'index', ['css', 'less']) - [indexStylesheet] + if @bundledPackage and @packageManager.packagesCache[@name].styleSheetsPaths? + styleSheetsPaths = @packageManager.packagesCache[@name].styleSheetsPaths + styleSheetsPaths.map (styleSheetPath) => path.join(@path, styleSheetPath) else - fs.listSync(stylesheetDirPath, ['css', 'less']) + stylesheetDirPath = @getStylesheetsPath() + if @metadata.mainStyleSheet + [fs.resolve(@path, @metadata.mainStyleSheet)] + else if @metadata.styleSheets + @metadata.styleSheets.map (name) -> fs.resolve(stylesheetDirPath, name, ['css', 'less', '']) + else if indexStylesheet = fs.resolve(@path, 'index', ['css', 'less']) + [indexStylesheet] + else + fs.listSync(stylesheetDirPath, ['css', 'less']) loadGrammarsSync: -> return if @grammarsLoaded From c156c9b7ea6ec28d44c1807c5ad22562da930c8f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 20 Mar 2017 10:11:04 +0100 Subject: [PATCH 15/17] :arrow_up: less-cache --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6450a5eb..cee84f7c7 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "jasmine-tagged": "^1.1.4", "jquery": "2.1.4", "key-path-helpers": "^0.4.0", - "less-cache": "1.0.0", + "less-cache": "1.1.0", "line-top-index": "0.2.0", "marked": "^0.3.6", "minimatch": "^3.0.3", From e2637aac780b6b634f4cebaabd010beb8c5e04ca Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 20 Mar 2017 10:20:36 +0100 Subject: [PATCH 16/17] :art: --- script/lib/generate-metadata.js | 2 +- src/package.coffee | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/script/lib/generate-metadata.js b/script/lib/generate-metadata.js index bd6872f3a..eec39d5c1 100644 --- a/script/lib/generate-metadata.js +++ b/script/lib/generate-metadata.js @@ -93,7 +93,7 @@ function buildBundledPackagesMetadata () { } } - packageNewMetadata.styleSheetsPaths = + packageNewMetadata.styleSheetPaths = styleSheets.map(styleSheetPath => path.relative(packagePath, styleSheetPath)) packages[packageMetadata.name] = packageNewMetadata diff --git a/src/package.coffee b/src/package.coffee index 21e161a26..d7de66e18 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -358,9 +358,9 @@ class Package path.join(@path, 'styles') getStylesheetPaths: -> - if @bundledPackage and @packageManager.packagesCache[@name].styleSheetsPaths? - styleSheetsPaths = @packageManager.packagesCache[@name].styleSheetsPaths - styleSheetsPaths.map (styleSheetPath) => path.join(@path, styleSheetPath) + if @bundledPackage and @packageManager.packagesCache[@name].styleSheetPaths? + styleSheetPaths = @packageManager.packagesCache[@name].styleSheetPaths + styleSheetPaths.map (styleSheetPath) => path.join(@path, styleSheetPath) else stylesheetDirPath = @getStylesheetsPath() if @metadata.mainStyleSheet From 2ce66a724c97c3e064643a02962c3c0632b6a55d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 20 Mar 2017 17:23:06 +0100 Subject: [PATCH 17/17] Ensure `packagesCache` exists before accessing it --- src/package.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.coffee b/src/package.coffee index d7de66e18..33f64c12d 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -358,7 +358,7 @@ class Package path.join(@path, 'styles') getStylesheetPaths: -> - if @bundledPackage and @packageManager.packagesCache[@name].styleSheetPaths? + if @bundledPackage and @packageManager.packagesCache[@name]?.styleSheetPaths? styleSheetPaths = @packageManager.packagesCache[@name].styleSheetPaths styleSheetPaths.map (styleSheetPath) => path.join(@path, styleSheetPath) else