➡️ Migrate core package 'dev-live-reload' into ./packages

This commit is contained in:
David Wilson 2018-09-28 10:01:25 -07:00
parent 0d006f22d9
commit 1904c01d5d
54 changed files with 1134 additions and 6 deletions

3
package-lock.json generated
View File

@ -1769,8 +1769,7 @@
}
},
"dev-live-reload": {
"version": "https://www.atom.io/api/packages/dev-live-reload/versions/0.48.1/tarball",
"integrity": "sha512-YSOLkdz7d/pETiG3raCzRKFmv64aErVC2d0cwDi7SLGtJyIGoR9+0OHMZjl8kcXCrX+u1s7awD8HhTDfZ56+iw==",
"version": "file:packages/dev-live-reload",
"requires": {
"fs-plus": "^3.0.0"
}

View File

@ -50,7 +50,7 @@
"dalek": "https://www.atom.io/api/packages/dalek/versions/0.2.2/tarball",
"dedent": "^0.7.0",
"deprecation-cop": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball",
"dev-live-reload": "https://www.atom.io/api/packages/dev-live-reload/versions/0.48.1/tarball",
"dev-live-reload": "file:packages/dev-live-reload",
"devtron": "1.3.0",
"encoding-selector": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball",
"etch": "^0.12.6",
@ -197,7 +197,7 @@
"command-palette": "0.43.5",
"dalek": "0.2.2",
"deprecation-cop": "0.56.9",
"dev-live-reload": "0.48.1",
"dev-live-reload": "file:./packages/dev-live-reload",
"encoding-selector": "0.23.9",
"exception-reporting": "file:./packages/exception-reporting",
"find-and-replace": "0.215.14",

View File

@ -28,7 +28,7 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
| **command-palette** | [`atom/command-palette`][command-palette] | |
| **dalek** | [`atom/dalek`][dalek] | [#17838](https://github.com/atom/atom/issues/17838) |
| **deprecation-cop** | [`atom/deprecation-cop`][deprecation-cop] | [#17839](https://github.com/atom/atom/issues/17839) |
| **dev-live-reload** | [`atom/dev-live-reload`][dev-live-reload] | [#17840](https://github.com/atom/atom/issues/17840) |
| **dev-live-reload** | [`./dev-live-reload`](dev-live-reload) | [#17840](https://github.com/atom/atom/issues/17840) |
| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) |
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | [#17842](https://github.com/atom/atom/issues/17842) |
| **find-and-replace** | [`atom/find-and-replace`][find-and-replace] | |
@ -115,7 +115,6 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
[command-palette]: https://github.com/atom/command-palette
[dalek]: https://github.com/atom/dalek
[deprecation-cop]: https://github.com/atom/deprecation-cop
[dev-live-reload]: https://github.com/atom/dev-live-reload
[encoding-selector]: https://github.com/atom/encoding-selector
[find-and-replace]: https://github.com/atom/find-and-replace
[fuzzy-finder]: https://github.com/atom/fuzzy-finder

1
packages/dev-live-reload/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -0,0 +1,20 @@
Copyright (c) 2014 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,13 @@
# Dev Live Reload package
This live reloads the Atom `.less` files. You edit styles and they are magically reflected in any running Atom windows. Magic! :tophat: :sparkles: :rabbit2:
Installed by default on Atom windows running in dev mode. Use the "Application: Open Dev" command to open a new dev mode window.
Use <kbd>meta-shift-ctrl-r</kbd> to reload all core and package stylesheets.
This package is __experimental__, it does not handle the following:
* File additions to a theme. New files will not be watched.
![gif](https://f.cloud.github.com/assets/69169/1387004/d2dc45f2-3b84-11e3-877e-cac8c51e9702.gif)

View File

@ -0,0 +1,5 @@
'.platform-darwin':
'cmd-ctrl-R': 'dev-live-reload:reload-all'
'.platform-win32':
'alt-ctrl-R': 'dev-live-reload:reload-all'

View File

@ -0,0 +1,28 @@
const fs = require('fs-plus')
const path = require('path')
const Watcher = require('./watcher')
module.exports =
class BaseThemeWatcher extends Watcher {
constructor () {
super()
this.stylesheetsPath = path.dirname(atom.themes.resolveStylesheet('../static/atom.less'))
this.watch()
}
watch () {
const filePaths = fs.readdirSync(this.stylesheetsPath).filter(filePath => path.extname(filePath).includes('less'))
for (const filePath of filePaths) {
this.watchFile(path.join(this.stylesheetsPath, filePath))
}
}
loadStylesheet () {
this.loadAllStylesheets()
}
loadAllStylesheets () {
atom.themes.reloadBaseStylesheets()
}
}

View File

@ -0,0 +1,24 @@
module.exports = {
activate (state) {
if (!atom.inDevMode() || atom.inSpecMode()) return
if (atom.packages.hasActivatedInitialPackages()) {
this.startWatching()
} else {
this.activatedDisposable = atom.packages.onDidActivateInitialPackages(() => this.startWatching())
}
},
deactivate () {
if (this.activatedDisposable) this.activatedDisposable.dispose()
if (this.commandDisposable) this.commandDisposable.dispose()
if (this.uiWatcher) this.uiWatcher.destroy()
},
startWatching () {
const UIWatcher = require('./ui-watcher')
this.uiWatcher = new UIWatcher({themeManager: atom.themes})
this.commandDisposable = atom.commands.add('atom-workspace', 'dev-live-reload:reload-all', () => this.uiWatcher.reloadAll())
if (this.activatedDisposable) this.activatedDisposable.dispose()
}
}

View File

@ -0,0 +1,48 @@
const fs = require('fs-plus')
const Watcher = require('./watcher')
module.exports =
class PackageWatcher extends Watcher {
static supportsPackage (pack, type) {
if (pack.getType() === type && pack.getStylesheetPaths().length) return true
return false
}
constructor (pack) {
super()
this.pack = pack
this.watch()
}
watch () {
const watchedPaths = []
const watchPath = stylesheet => {
if (!watchedPaths.includes(stylesheet)) this.watchFile(stylesheet)
watchedPaths.push(stylesheet)
}
const stylesheetsPath = this.pack.getStylesheetsPath()
if (fs.isDirectorySync(stylesheetsPath)) this.watchDirectory(stylesheetsPath)
const stylesheetPaths = new Set(this.pack.getStylesheetPaths())
const onFile = stylesheetPath => stylesheetPaths.add(stylesheetPath)
const onFolder = () => true
fs.traverseTreeSync(stylesheetsPath, onFile, onFolder)
for (let stylesheet of stylesheetPaths) {
watchPath(stylesheet)
}
}
loadStylesheet (pathName) {
if (pathName.includes('variables')) this.emitGlobalsChanged()
this.loadAllStylesheets()
}
loadAllStylesheets () {
console.log('Reloading package', this.pack.name)
this.pack.reloadStylesheets()
}
}

View File

@ -0,0 +1,81 @@
const {CompositeDisposable} = require('atom')
const BaseThemeWatcher = require('./base-theme-watcher')
const PackageWatcher = require('./package-watcher')
module.exports =
class UIWatcher {
constructor () {
this.subscriptions = new CompositeDisposable()
this.reloadAll = this.reloadAll.bind(this)
this.watchers = []
this.baseTheme = this.createWatcher(new BaseThemeWatcher())
this.watchPackages()
}
watchPackages () {
this.watchedThemes = new Map()
this.watchedPackages = new Map()
for (const theme of atom.themes.getActiveThemes()) { this.watchTheme(theme) }
for (const pack of atom.packages.getActivePackages()) { this.watchPackage(pack) }
this.watchForPackageChanges()
}
watchForPackageChanges () {
this.subscriptions.add(atom.themes.onDidChangeActiveThemes(() => {
// We need to destroy all theme watchers as all theme packages are destroyed
// when a theme changes.
for (const theme of this.watchedThemes.values()) { theme.destroy() }
this.watchedThemes.clear()
// Rewatch everything!
for (const theme of atom.themes.getActiveThemes()) { this.watchTheme(theme) }
}))
this.subscriptions.add(atom.packages.onDidActivatePackage(pack => this.watchPackage(pack)))
this.subscriptions.add(atom.packages.onDidDeactivatePackage(pack => {
// This only handles packages - onDidChangeActiveThemes handles themes
const watcher = this.watchedPackages.get(pack.name)
if (watcher) watcher.destroy()
this.watchedPackages.delete(pack.name)
}))
}
watchTheme (theme) {
if (PackageWatcher.supportsPackage(theme, 'theme')) this.watchedThemes.set(theme.name, this.createWatcher(new PackageWatcher(theme)))
}
watchPackage (pack) {
if (PackageWatcher.supportsPackage(pack, 'atom')) this.watchedPackages.set(pack.name, this.createWatcher(new PackageWatcher(pack)))
}
createWatcher (watcher) {
watcher.onDidChangeGlobals(() => {
console.log('Global changed, reloading all styles')
this.reloadAll()
})
watcher.onDidDestroy(() => this.watchers.splice(this.watchers.indexOf(watcher), 1))
this.watchers.push(watcher)
return watcher
}
reloadAll () {
this.baseTheme.loadAllStylesheets()
for (const pack of atom.packages.getActivePackages()) {
if (PackageWatcher.supportsPackage(pack, 'atom')) pack.reloadStylesheets()
}
for (const theme of atom.themes.getActiveThemes()) {
if (PackageWatcher.supportsPackage(theme, 'theme')) theme.reloadStylesheets()
}
}
destroy () {
this.subscriptions.dispose()
this.baseTheme.destroy()
for (const pack of this.watchedPackages.values()) { pack.destroy() }
for (const theme of this.watchedThemes.values()) { theme.destroy() }
}
}

View File

@ -0,0 +1,72 @@
const {CompositeDisposable, File, Directory, Emitter} = require('atom')
const path = require('path')
module.exports =
class Watcher {
constructor () {
this.destroy = this.destroy.bind(this)
this.emitter = new Emitter()
this.disposables = new CompositeDisposable()
this.entities = [] // Used for specs
}
onDidDestroy (callback) {
this.emitter.on('did-destroy', callback)
}
onDidChangeGlobals (callback) {
this.emitter.on('did-change-globals', callback)
}
destroy () {
this.disposables.dispose()
this.entities = null
this.emitter.emit('did-destroy')
this.emitter.dispose()
}
watch () {
// override me
}
loadStylesheet (stylesheetPath) {
// override me
}
loadAllStylesheets () {
// override me
}
emitGlobalsChanged () {
this.emitter.emit('did-change-globals')
}
watchDirectory (directoryPath) {
if (this.isInAsarArchive(directoryPath)) return
const entity = new Directory(directoryPath)
this.disposables.add(entity.onDidChange(() => this.loadAllStylesheets()))
this.entities.push(entity)
}
watchGlobalFile (filePath) {
const entity = new File(filePath)
this.disposables.add(entity.onDidChange(() => this.emitGlobalsChanged()))
this.entities.push(entity)
}
watchFile (filePath) {
if (this.isInAsarArchive(filePath)) return
const reloadFn = () => this.loadStylesheet(entity.getPath())
const entity = new File(filePath)
this.disposables.add(entity.onDidChange(reloadFn))
this.disposables.add(entity.onDidDelete(reloadFn))
this.disposables.add(entity.onDidRename(reloadFn))
this.entities.push(entity)
}
isInAsarArchive (pathToCheck) {
const {resourcePath} = atom.getLoadSettings()
return pathToCheck.startsWith(`${resourcePath}${path.sep}`) && path.extname(resourcePath) === '.asar'
}
}

View File

@ -0,0 +1,9 @@
'menu': [
'label': 'Packages'
'submenu': [
'label': 'Dev Live Reload'
'submenu': [
{ 'label': 'Reload All Styles', 'command': 'dev-live-reload:reload-all' }
]
]
]

View File

@ -0,0 +1,28 @@
{
"name": "dev-live-reload",
"main": "./lib/main",
"version": "0.48.1",
"description": "Live reload atom themes and packages.",
"repository": "https://github.com/atom/atom",
"license": "MIT",
"dependencies": {
"fs-plus": "^3.0.0"
},
"engines": {
"atom": "*"
},
"devDependencies": {
"standard": "^10.0.3"
},
"standard": {
"env": {
"atomtest": true,
"browser": true,
"jasmine": true,
"node": true
},
"globals": [
"atom"
]
}
}

View File

@ -0,0 +1,103 @@
/** @babel */
export function beforeEach (fn) {
global.beforeEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
export function afterEach (fn) {
global.afterEach(function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
['it', 'fit', 'ffit', 'fffit'].forEach(function (name) {
module.exports[name] = function (description, fn) {
if (fn === undefined) {
global[name](description)
return
}
global[name](description, function () {
const result = fn()
if (result instanceof Promise) {
waitsForPromise(() => result)
}
})
}
})
export async function conditionPromise (condition, description = 'anonymous condition') {
const startTime = Date.now()
while (true) {
await timeoutPromise(100)
if (await condition()) {
return
}
if (Date.now() - startTime > 5000) {
throw new Error('Timed out waiting on ' + description)
}
}
}
export function timeoutPromise (timeout) {
return new Promise(function (resolve) {
global.setTimeout(resolve, timeout)
})
}
function waitsForPromise (fn) {
const promise = fn()
global.waitsFor('spec promise to resolve', function (done) {
promise.then(done, function (error) {
jasmine.getEnv().currentSpec.fail(error)
done()
})
})
}
export function emitterEventPromise (emitter, event, timeout = 15000) {
return new Promise((resolve, reject) => {
const timeoutHandle = setTimeout(() => {
reject(new Error(`Timed out waiting for '${event}' event`))
}, timeout)
emitter.once(event, () => {
clearTimeout(timeoutHandle)
resolve()
})
})
}
export function promisify (original) {
return function (...args) {
return new Promise((resolve, reject) => {
args.push((err, ...results) => {
if (err) {
reject(err)
} else {
resolve(...results)
}
})
return original(...args)
})
}
}
export function promisifySome (obj, fnNames) {
const result = {}
for (const fnName of fnNames) {
result[fnName] = promisify(obj[fnName])
}
return result
}

View File

@ -0,0 +1,124 @@
const {it, fit, ffit, afterEach, beforeEach} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
describe('Dev Live Reload', () => {
describe('package activation', () => {
let [pack, mainModule] = []
beforeEach(() => {
pack = atom.packages.loadPackage('dev-live-reload')
pack.requireMainModule()
mainModule = pack.mainModule
spyOn(mainModule, 'startWatching')
})
describe('when the window is not in dev mode', () => {
beforeEach(() => spyOn(atom, 'inDevMode').andReturn(false))
it('does not watch files', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.startWatching).not.toHaveBeenCalled()
})
})
describe('when the window is in spec mode', () => {
beforeEach(() => spyOn(atom, 'inSpecMode').andReturn(true))
it('does not watch files', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.startWatching).not.toHaveBeenCalled()
})
})
describe('when the window is in dev mode', () => {
beforeEach(() => {
spyOn(atom, 'inDevMode').andReturn(true)
spyOn(atom, 'inSpecMode').andReturn(false)
})
it('watches files', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.startWatching).toHaveBeenCalled()
})
})
describe('when the window is in both dev mode and spec mode', () => {
beforeEach(() => {
spyOn(atom, 'inDevMode').andReturn(true)
spyOn(atom, 'inSpecMode').andReturn(true)
})
it('does not watch files', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.startWatching).not.toHaveBeenCalled()
})
})
describe('when the package is activated before initial packages have been activated', () => {
beforeEach(() => {
spyOn(atom, 'inDevMode').andReturn(true)
spyOn(atom, 'inSpecMode').andReturn(false)
})
it('waits until all initial packages have been activated before watching files', async () => {
await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.startWatching).not.toHaveBeenCalled()
atom.packages.emitter.emit('did-activate-initial-packages')
expect(mainModule.startWatching).toHaveBeenCalled()
})
})
})
describe('package deactivation', () => {
beforeEach(() => {
spyOn(atom, 'inDevMode').andReturn(true)
spyOn(atom, 'inSpecMode').andReturn(false)
})
it('stops watching all files', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
const {mainModule} = await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.uiWatcher).not.toBeNull()
spyOn(mainModule.uiWatcher, 'destroy')
await atom.packages.deactivatePackage('dev-live-reload')
expect(mainModule.uiWatcher.destroy).toHaveBeenCalled()
})
it('unsubscribes from the onDidActivateInitialPackages subscription if it is disabled before all initial packages are activated', async () => {
const {mainModule} = await atom.packages.activatePackage('dev-live-reload')
expect(mainModule.activatedDisposable.disposed).toBe(false)
await atom.packages.deactivatePackage('dev-live-reload')
expect(mainModule.activatedDisposable.disposed).toBe(true)
spyOn(mainModule, 'startWatching')
atom.packages.emitter.emit('did-activate-initial-packages')
expect(mainModule.startWatching).not.toHaveBeenCalled()
})
it('removes its commands', async () => {
spyOn(atom.packages, 'hasActivatedInitialPackages').andReturn(true)
await atom.packages.activatePackage('dev-live-reload')
expect(atom.commands
.findCommands({target: atom.views.getView(atom.workspace)})
.filter(command => command.name.startsWith('dev-live-reload'))
.length).toBeGreaterThan(0)
await atom.packages.deactivatePackage('dev-live-reload')
expect(atom.commands
.findCommands({target: atom.views.getView(atom.workspace)})
.filter(command => command.name.startsWith('dev-live-reload'))
.length).toBe(0)
})
})
})

View File

@ -0,0 +1,2 @@
module.exports =
activate: ->

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 3px;
}

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 1px;
}

View File

@ -0,0 +1,5 @@
@size: 2px;
#jasmine-content {
font-size: @size;
}

View File

@ -0,0 +1 @@
styleSheets: ['2', '1']

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 1px;
}

View File

@ -0,0 +1,5 @@
@size: 2px;
#jasmine-content {
font-size: @size;
}

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 3px;
}

View File

@ -0,0 +1,3 @@
@import "styles/first";
@import "styles/second";
@import "styles/last";

View File

@ -0,0 +1 @@
styleSheets: ['2', '1']

View File

@ -0,0 +1,3 @@
{
"theme": true
}

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 1px;
}

View File

@ -0,0 +1,5 @@
@size: 2px;
#jasmine-content {
font-size: @size;
}

View File

@ -0,0 +1,3 @@
#jasmine-content {
font-size: 3px;
}

View File

@ -0,0 +1,7 @@
.editor {
padding-top: 101px;
padding-right: 101px;
padding-bottom: 101px;
color: red;
}

View File

@ -0,0 +1,5 @@
.editor {
/* padding-top: 103px;
padding-right: 103px;*/
padding-bottom: 103px;
}

View File

@ -0,0 +1,9 @@
@import "ui-variables";
@number: 102px;
.editor {
/* padding-top: 102px;*/
padding-right: @number;
padding-bottom: @number;
}

View File

@ -0,0 +1,75 @@
// Variables different from the original are marked 'Changed'
@text-color: #333;
@text-color-subtle: #777;
@text-color-highlight: #111;
@text-color-selected: @text-color-highlight;
@text-color-info: #5293d8;
@text-color-success: #1fe977;
@text-color-warning: #f78a46;
@text-color-error: #c00;
@background-color-info: #0098ff;
@background-color-success: #17ca65;
@background-color-warning: #ff4800;
@background-color-error: #c00;
@background-color-highlight: rgba(255, 255, 255, 0.10);
@background-color-selected: @background-color-highlight;
@app-background-color: #00f; // Changed
@base-background-color: #fff;
@base-border-color: #eee;
@pane-item-background-color: @base-background-color;
@pane-item-border-color: @base-border-color;
@input-background-color: #f00; // Changed
@input-border-color: @base-border-color;
@tool-panel-background-color: #f4f4f4;
@tool-panel-border-color: @base-border-color;
@inset-panel-background-color: #eee;
@inset-panel-border-color: @base-border-color;
@panel-heading-background-color: #ddd;
@panel-heading-border-color: transparent;
@overlay-background-color: #f4f4f4;
@overlay-border-color: @base-border-color;
@button-background-color: #ccc;
@button-background-color-hover: lighten(@button-background-color, 5%);
@button-background-color-selected: @button-background-color-hover;
@button-border-color: #aaa;
@tab-bar-background-color: #fff;
@tab-bar-border-color: darken(@tab-background-color-active, 10%);
@tab-background-color: #f4f4f4;
@tab-background-color-active: #fff;
@tab-border-color: @base-border-color;
@tree-view-background-color: @tool-panel-background-color;
@tree-view-border-color: @tool-panel-border-color;
@ui-site-color-1: @background-color-success; // green
@ui-site-color-2: @background-color-info; // blue
@ui-site-color-3: @background-color-warning; // orange
@ui-site-color-4: #db2ff4; // purple
@ui-site-color-5: #f5e11d; // yellow
@font-size: 12px;
@disclosure-arrow-size: 12px;
@component-padding: 150px;
@component-icon-padding: 5px;
@component-icon-size: 16px;
@component-line-height: 25px;
@component-border-radius: 2px;
@tab-height: 30px;
@font-family: Arial;

View File

@ -0,0 +1,5 @@
@padding: 4321px;
atom-text-editor {
padding-top: @padding;
}

View File

@ -0,0 +1,3 @@
{
"theme": "ui"
}

View File

@ -0,0 +1,3 @@
@import "styles/first";
@import "styles/second";
@import "styles/last";

View File

@ -0,0 +1,4 @@
{
"name": "theme-with-multiple-imported-files",
"theme": "ui"
}

View File

@ -0,0 +1,7 @@
.editor {
padding-top: 101px;
padding-right: 101px;
padding-bottom: 101px;
color: red;
}

View File

@ -0,0 +1,5 @@
.editor {
/* padding-top: 103px;
padding-right: 103px;*/
padding-bottom: 103px;
}

View File

@ -0,0 +1,9 @@
@import "ui-variables";
@number: 102px;
.editor {
/* padding-top: 102px;*/
padding-right: @number;
padding-bottom: @number;
}

View File

@ -0,0 +1,75 @@
// Variables different from the original are marked 'Changed'
@text-color: #333;
@text-color-subtle: #777;
@text-color-highlight: #111;
@text-color-selected: @text-color-highlight;
@text-color-info: #5293d8;
@text-color-success: #1fe977;
@text-color-warning: #f78a46;
@text-color-error: #c00;
@background-color-info: #0098ff;
@background-color-success: #17ca65;
@background-color-warning: #ff4800;
@background-color-error: #c00;
@background-color-highlight: rgba(255, 255, 255, 0.10);
@background-color-selected: @background-color-highlight;
@app-background-color: #00f; // Changed
@base-background-color: #fff;
@base-border-color: #eee;
@pane-item-background-color: @base-background-color;
@pane-item-border-color: @base-border-color;
@input-background-color: #f00; // Changed
@input-border-color: @base-border-color;
@tool-panel-background-color: #f4f4f4;
@tool-panel-border-color: @base-border-color;
@inset-panel-background-color: #eee;
@inset-panel-border-color: @base-border-color;
@panel-heading-background-color: #ddd;
@panel-heading-border-color: transparent;
@overlay-background-color: #f4f4f4;
@overlay-border-color: @base-border-color;
@button-background-color: #ccc;
@button-background-color-hover: lighten(@button-background-color, 5%);
@button-background-color-selected: @button-background-color-hover;
@button-border-color: #aaa;
@tab-bar-background-color: #fff;
@tab-bar-border-color: darken(@tab-background-color-active, 10%);
@tab-background-color: #f4f4f4;
@tab-background-color-active: #fff;
@tab-border-color: @base-border-color;
@tree-view-background-color: @tool-panel-background-color;
@tree-view-border-color: @tool-panel-border-color;
@ui-site-color-1: @background-color-success; // green
@ui-site-color-2: @background-color-info; // blue
@ui-site-color-3: @background-color-warning; // orange
@ui-site-color-4: #db2ff4; // purple
@ui-site-color-5: #f5e11d; // yellow
@font-size: 12px;
@disclosure-arrow-size: 12px;
@component-padding: 150px;
@component-icon-padding: 5px;
@component-icon-size: 16px;
@component-line-height: 25px;
@component-border-radius: 2px;
@tab-height: 30px;
@font-family: Arial;

View File

@ -0,0 +1,4 @@
{
"theme": "ui",
"styleSheets": ["first.css", "second.less", "last.css"]
}

View File

@ -0,0 +1,7 @@
atom-text-editor {
padding-top: 101px;
padding-right: 101px;
padding-bottom: 101px;
color: red;
}

View File

@ -0,0 +1,5 @@
atom-text-editor {
/* padding-top: 103px;
padding-right: 103px;*/
padding-bottom: 103px;
}

View File

@ -0,0 +1,7 @@
@number: 102px;
atom-text-editor {
/* padding-top: 102px;*/
padding-right: @number;
padding-bottom: @number;
}

View File

@ -0,0 +1,4 @@
{
"theme": "syntax",
"styleSheets": ["editor.less"]
}

View File

@ -0,0 +1,4 @@
{
"theme": "ui",
"styleSheets": ["editor.less"]
}

View File

@ -0,0 +1,9 @@
@import "ui-variables";
atom-text-editor {
padding-top: @component-padding;
padding-right: @component-padding;
padding-bottom: @component-padding;
color: @input-background-color;
}

View File

@ -0,0 +1,75 @@
// Variables different from the original are marked 'Changed'
@text-color: #333;
@text-color-subtle: #777;
@text-color-highlight: #111;
@text-color-selected: @text-color-highlight;
@text-color-info: #5293d8;
@text-color-success: #1fe977;
@text-color-warning: #f78a46;
@text-color-error: #c00;
@background-color-info: #0098ff;
@background-color-success: #17ca65;
@background-color-warning: #ff4800;
@background-color-error: #c00;
@background-color-highlight: rgba(255, 255, 255, 0.10);
@background-color-selected: @background-color-highlight;
@app-background-color: #00f; // Changed
@base-background-color: #fff;
@base-border-color: #eee;
@pane-item-background-color: @base-background-color;
@pane-item-border-color: @base-border-color;
@input-background-color: #f00; // Changed
@input-border-color: @base-border-color;
@tool-panel-background-color: #f4f4f4;
@tool-panel-border-color: @base-border-color;
@inset-panel-background-color: #eee;
@inset-panel-border-color: @base-border-color;
@panel-heading-background-color: #ddd;
@panel-heading-border-color: transparent;
@overlay-background-color: #f4f4f4;
@overlay-border-color: @base-border-color;
@button-background-color: #ccc;
@button-background-color-hover: lighten(@button-background-color, 5%);
@button-background-color-selected: @button-background-color-hover;
@button-border-color: #aaa;
@tab-bar-background-color: #fff;
@tab-bar-border-color: darken(@tab-background-color-active, 10%);
@tab-background-color: #f4f4f4;
@tab-background-color-active: #fff;
@tab-border-color: @base-border-color;
@tree-view-background-color: @tool-panel-background-color;
@tree-view-border-color: @tool-panel-border-color;
@ui-site-color-1: @background-color-success; // green
@ui-site-color-2: @background-color-info; // blue
@ui-site-color-3: @background-color-warning; // orange
@ui-site-color-4: #db2ff4; // purple
@ui-site-color-5: #f5e11d; // yellow
@font-size: 12px;
@disclosure-arrow-size: 12px;
@component-padding: 150px;
@component-icon-padding: 5px;
@component-icon-size: 16px;
@component-line-height: 25px;
@component-border-radius: 2px;
@tab-height: 30px;
@font-family: Arial;

View File

@ -0,0 +1,208 @@
const path = require('path')
const UIWatcher = require('../lib/ui-watcher')
const {it, fit, ffit, afterEach, beforeEach, conditionPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars
describe('UIWatcher', () => {
let uiWatcher = null
beforeEach(() => atom.packages.packageDirPaths.push(path.join(__dirname, 'fixtures')))
afterEach(() => uiWatcher && uiWatcher.destroy())
describe("when a base theme's file changes", () => {
beforeEach(() => {
spyOn(atom.themes, 'resolveStylesheet').andReturn(path.join(__dirname, 'fixtures', 'static', 'atom.less'))
uiWatcher = new UIWatcher()
})
it('reloads all the base styles', () => {
spyOn(atom.themes, 'reloadBaseStylesheets')
expect(uiWatcher.baseTheme.entities[0].getPath()).toContain(`${path.sep}static${path.sep}`)
uiWatcher.baseTheme.entities[0].emitter.emit('did-change')
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled()
})
})
it("watches all the style sheets in the theme's styles folder", async () => {
const packagePath = path.join(__dirname, 'fixtures', 'package-with-styles-folder')
await atom.packages.activatePackage(packagePath)
uiWatcher = new UIWatcher()
const lastWatcher = uiWatcher.watchers[uiWatcher.watchers.length - 1]
expect(lastWatcher.entities.length).toBe(4)
expect(lastWatcher.entities[0].getPath()).toBe(path.join(packagePath, 'styles'))
expect(lastWatcher.entities[1].getPath()).toBe(path.join(packagePath, 'styles', '3.css'))
expect(lastWatcher.entities[2].getPath()).toBe(path.join(packagePath, 'styles', 'sub', '1.css'))
expect(lastWatcher.entities[3].getPath()).toBe(path.join(packagePath, 'styles', 'sub', '2.less'))
})
describe('when a package stylesheet file changes', async () => {
beforeEach(async () => {
await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-manifest'))
uiWatcher = new UIWatcher()
})
it('reloads all package styles', () => {
const pack = atom.packages.getActivePackages()[0]
spyOn(pack, 'reloadStylesheets')
uiWatcher.watchers[uiWatcher.watchers.length - 1].entities[1].emitter.emit('did-change')
expect(pack.reloadStylesheets).toHaveBeenCalled()
})
})
describe('when a package does not have a stylesheet', () => {
beforeEach(async () => {
await atom.packages.activatePackage('package-with-index')
uiWatcher = new UIWatcher()
})
it('does not create a PackageWatcher', () => {
expect(uiWatcher.watchedPackages['package-with-index']).toBeUndefined()
})
})
describe('when a package global file changes', () => {
beforeEach(async () => {
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-multiple-imported-files'])
await atom.themes.activateThemes()
uiWatcher = new UIWatcher()
})
afterEach(() => atom.themes.deactivateThemes())
it('reloads every package when the variables file changes', () => {
let varEntity
for (const theme of atom.themes.getActiveThemes()) {
spyOn(theme, 'reloadStylesheets')
}
for (const entity of uiWatcher.watchedThemes.get('theme-with-multiple-imported-files').entities) {
if (entity.getPath().indexOf('variables') > -1) varEntity = entity
}
varEntity.emitter.emit('did-change')
for (const theme of atom.themes.getActiveThemes()) {
expect(theme.reloadStylesheets).toHaveBeenCalled()
}
})
})
describe('watcher lifecycle', () => {
it('starts watching a package if it is activated after initial startup', async () => {
uiWatcher = new UIWatcher()
expect(uiWatcher.watchedPackages.size).toBe(0)
await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder'))
expect(uiWatcher.watchedPackages.get('package-with-styles-folder')).not.toBeUndefined()
})
it('unwatches a package after it is deactivated', async () => {
await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder'))
uiWatcher = new UIWatcher()
const watcher = uiWatcher.watchedPackages.get('package-with-styles-folder')
expect(watcher).not.toBeUndefined()
const watcherDestructionSpy = jasmine.createSpy('watcher-on-did-destroy')
watcher.onDidDestroy(watcherDestructionSpy)
await atom.packages.deactivatePackage('package-with-styles-folder')
expect(uiWatcher.watchedPackages.get('package-with-styles-folder')).toBeUndefined()
expect(uiWatcher.watchedPackages.size).toBe(0)
expect(watcherDestructionSpy).toHaveBeenCalled()
})
it('does not watch activated packages after the UI watcher has been destroyed', async () => {
uiWatcher = new UIWatcher()
uiWatcher.destroy()
await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'package-with-styles-folder'))
expect(uiWatcher.watchedPackages.size).toBe(0)
})
})
describe('minimal theme packages', () => {
let pack = null
beforeEach(async () => {
atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-index-less'])
await atom.themes.activateThemes()
uiWatcher = new UIWatcher()
pack = atom.themes.getActiveThemes()[0]
})
afterEach(() => atom.themes.deactivateThemes())
it('watches themes without a styles directory', () => {
spyOn(pack, 'reloadStylesheets')
spyOn(atom.themes, 'reloadBaseStylesheets')
const watcher = uiWatcher.watchedThemes.get('theme-with-index-less')
expect(watcher.entities.length).toBe(1)
watcher.entities[0].emitter.emit('did-change')
expect(pack.reloadStylesheets).toHaveBeenCalled()
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled()
})
})
describe('theme packages', () => {
let pack = null
beforeEach(async () => {
atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-multiple-imported-files'])
await atom.themes.activateThemes()
uiWatcher = new UIWatcher()
pack = atom.themes.getActiveThemes()[0]
})
afterEach(() => atom.themes.deactivateThemes())
it('reloads the theme when anything within the theme changes', () => {
spyOn(pack, 'reloadStylesheets')
spyOn(atom.themes, 'reloadBaseStylesheets')
const watcher = uiWatcher.watchedThemes.get('theme-with-multiple-imported-files')
expect(watcher.entities.length).toBe(6)
watcher.entities[2].emitter.emit('did-change')
expect(pack.reloadStylesheets).toHaveBeenCalled()
expect(atom.themes.reloadBaseStylesheets).not.toHaveBeenCalled()
watcher.entities[watcher.entities.length - 1].emitter.emit('did-change')
expect(atom.themes.reloadBaseStylesheets).toHaveBeenCalled()
})
it('unwatches when a theme is deactivated', async () => {
jasmine.useRealClock()
atom.config.set('core.themes', [])
await conditionPromise(() => !uiWatcher.watchedThemes['theme-with-multiple-imported-files'])
})
it('watches a new theme when it is deactivated', async () => {
jasmine.useRealClock()
atom.config.set('core.themes', ['theme-with-syntax-variables', 'theme-with-package-file'])
await conditionPromise(() => uiWatcher.watchedThemes.get('theme-with-package-file'))
pack = atom.themes.getActiveThemes()[0]
spyOn(pack, 'reloadStylesheets')
expect(pack.name).toBe('theme-with-package-file')
const watcher = uiWatcher.watchedThemes.get('theme-with-package-file')
watcher.entities[2].emitter.emit('did-change')
expect(pack.reloadStylesheets).toHaveBeenCalled()
})
})
})