diff --git a/packages/background-tips/lib/background-tips-view.js b/packages/background-tips/lib/background-tips-view.js index d6efce494b..0088d97c15 100644 --- a/packages/background-tips/lib/background-tips-view.js +++ b/packages/background-tips/lib/background-tips-view.js @@ -1,156 +1,184 @@ -const _ = require('underscore-plus') -const {CompositeDisposable, Disposable} = require('atom') -const Tips = require('./tips') +const _ = require("underscore-plus"); +const { CompositeDisposable, Disposable } = require("atom"); const TEMPLATE = `\
\ -` - -module.exports = -class BackgroundTipsElement { - constructor () { - this.element = document.createElement('background-tips') - this.index = -1 - this.workspaceCenter = atom.workspace.getCenter() - - this.startDelay = 1000 - this.displayDuration = 10000 - this.fadeDuration = 300 - - this.disposables = new CompositeDisposable() - - const visibilityCallback = () => this.updateVisibility() - - this.disposables.add(this.workspaceCenter.onDidAddPane(visibilityCallback)) - this.disposables.add(this.workspaceCenter.onDidDestroyPane(visibilityCallback)) - this.disposables.add(this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback)) - - atom.getCurrentWindow().on('blur', visibilityCallback) - atom.getCurrentWindow().on('focus', visibilityCallback) - - this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('blur', visibilityCallback))) - this.disposables.add(new Disposable(() => atom.getCurrentWindow().removeListener('focus', visibilityCallback))) - - this.startTimeout = setTimeout(() => this.start(), this.startDelay) +`; + +module.exports = class BackgroundTipsElement { + constructor(main) { + this.main = main; + this.element = document.createElement("background-tips"); + this.index = -1; + this.workspaceCenter = atom.workspace.getCenter(); + + this.startDelay = 1000; + this.displayDuration = 10000; + this.fadeDuration = 300; + + this.renderedTips = []; + this.tipsRendered = false; + + this.disposables = new CompositeDisposable(); + + const visibilityCallback = () => this.updateVisibility(); + + this.disposables.add(this.workspaceCenter.onDidAddPane(visibilityCallback)); + this.disposables.add( + this.workspaceCenter.onDidDestroyPane(visibilityCallback) + ); + this.disposables.add( + this.workspaceCenter.onDidChangeActivePaneItem(visibilityCallback) + ); + + atom.getCurrentWindow().on("blur", visibilityCallback); + atom.getCurrentWindow().on("focus", visibilityCallback); + + this.disposables.add( + new Disposable(() => + atom.getCurrentWindow().removeListener("blur", visibilityCallback) + ) + ); + this.disposables.add( + new Disposable(() => + atom.getCurrentWindow().removeListener("focus", visibilityCallback) + ) + ); + + this.startTimeout = setTimeout(() => this.start(), this.startDelay); } - destroy () { - this.stop() - this.disposables.dispose() + destroy() { + this.stop(); + this.disposables.dispose(); } - attach () { - this.element.innerHTML = TEMPLATE - this.message = this.element.querySelector('.message') + attach() { + this.element.innerHTML = TEMPLATE; + this.message = this.element.querySelector(".message"); - const paneView = atom.views.getView(this.workspaceCenter.getActivePane()) - const itemViews = paneView.querySelector('.item-views') - let top = 0 + const paneView = atom.views.getView(this.workspaceCenter.getActivePane()); + const itemViews = paneView.querySelector(".item-views"); + let top = 0; if (itemViews && itemViews.offsetTop) { - top = itemViews.offsetTop + top = itemViews.offsetTop; } - this.element.style.top = top + 'px' - paneView.appendChild(this.element) + this.element.style.top = top + "px"; + paneView.appendChild(this.element); } - detach () { - this.element.remove() + detach() { + this.element.remove(); } - updateVisibility () { + updateVisibility() { if (this.shouldBeAttached()) { - this.start() + this.start(); } else { - this.stop() + this.stop(); } } - shouldBeAttached () { - return this.workspaceCenter.getPanes().length === 1 && - this.workspaceCenter.getActivePaneItem() == null && - atom.getCurrentWindow().isFocused() + shouldBeAttached() { + return ( + this.workspaceCenter.getPanes().length === 1 && + this.workspaceCenter.getActivePaneItem() == null && + atom.getCurrentWindow().isFocused() + ); } - start () { - if (!this.shouldBeAttached() || this.interval != null) return - this.renderTips() - this.randomizeIndex() - this.attach() - this.showNextTip() - this.interval = setInterval(() => this.showNextTip(), this.displayDuration) + start() { + if (!this.shouldBeAttached() || this.interval != null) return; + this.renderTips(); + this.randomizeIndex(); + this.attach(); + this.showNextTip(); + this.interval = setInterval(() => this.showNextTip(), this.displayDuration); } - stop () { - this.element.remove() + stop() { + this.element.remove(); if (this.interval != null) { - clearInterval(this.interval) + clearInterval(this.interval); } - clearTimeout(this.startTimeout) - clearTimeout(this.nextTipTimeout) - this.interval = null + clearTimeout(this.startTimeout); + clearTimeout(this.nextTipTimeout); + this.interval = null; } - randomizeIndex () { - const len = Tips.length - this.index = Math.round(Math.random() * len) % len + randomizeIndex() { + const len = this.renderedTips.length; + this.index = len > 0 ? Math.round(Math.random() * len) % len : 0; } - showNextTip () { - this.index = ++this.index % Tips.length - this.message.classList.remove('fade-in') + showNextTip() { + if (this.renderedTips.length === 0) return; + this.index = ++this.index % this.renderedTips.length; + this.message.classList.remove("fade-in"); this.nextTipTimeout = setTimeout(() => { - this.message.innerHTML = Tips[this.index] - this.message.classList.add('fade-in') - }, this.fadeDuration) + this.message.innerHTML = this.renderedTips[this.index]; + this.message.classList.add("fade-in"); + }, this.fadeDuration); } - renderTips () { - if (this.tipsRendered) return - for (let i = 0; i < Tips.length; i++) { - const tip = Tips[i] - Tips[i] = this.renderTip(tip) + renderTips() { + if (this.tipsRendered) return; + const tips = this.main.getTips(); + this.renderedTips = tips.map((tip) => this.renderTip(tip)); + this.tipsRendered = true; + } + + tipsChanged() { + this.tipsRendered = false; + if (this.interval != null) { + this.renderTips(); + this.index = Math.min(this.index, this.renderedTips.length - 1); } - this.tipsRendered = true } - renderTip (str) { + renderTip(str) { str = str.replace(/\{(.+)\}/g, (match, command) => { - let binding, scope - const scopeAndCommand = command.split('>') + let binding, scope; + const scopeAndCommand = command.split(">"); if (scopeAndCommand.length > 1) { - [scope, command] = scopeAndCommand + [scope, command] = scopeAndCommand; } - const bindings = atom.keymaps.findKeyBindings({command: command.trim()}) + const bindings = atom.keymaps.findKeyBindings({ + command: command.trim(), + }); if (scope) { for (binding of bindings) { - if (binding.selector === scope) break + if (binding.selector === scope) break; } } else { - binding = this.getKeyBindingForCurrentPlatform(bindings) + binding = this.getKeyBindingForCurrentPlatform(bindings); } if (binding && binding.keystrokes) { - const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace(/\s+/g, ' ') - return `${keystrokeLabel}` + const keystrokeLabel = _.humanizeKeystroke(binding.keystrokes).replace( + /\s+/g, + " " + ); + return `${keystrokeLabel}`; } else { - return command + return command; } - }) - return str + }); + return str; } - getKeyBindingForCurrentPlatform (bindings) { - if (!bindings || !bindings.length) return + getKeyBindingForCurrentPlatform(bindings) { + if (!bindings || !bindings.length) return; for (let binding of bindings) { if (binding.selector.indexOf(process.platform) !== -1) { - return binding + return binding; } } - return bindings[0] + return bindings[0]; } -} +}; diff --git a/packages/background-tips/lib/background-tips.js b/packages/background-tips/lib/background-tips.js index 3c827be7e2..8e7f0d325c 100644 --- a/packages/background-tips/lib/background-tips.js +++ b/packages/background-tips/lib/background-tips.js @@ -1,11 +1,36 @@ -const BackgroundTipsView = require('./background-tips-view') +const { Disposable } = require("atom"); +const BackgroundTipsView = require("./background-tips-view"); module.exports = { - activate () { - this.backgroundTipsView = new BackgroundTipsView() + activate() { + this.defaultTips = require("./tips"); + this.addedTips = new Set(); + this.backgroundTipsView = new BackgroundTipsView(this); }, - deactivate () { - this.backgroundTipsView.destroy() - } -} + deactivate() { + this.backgroundTipsView.destroy(); + this.addedTips.clear(); + }, + + getTips() { + let all = [...this.defaultTips]; + for (const tips of this.addedTips) { + all = all.concat(tips); + } + return all; + }, + + provideBackgroundTips() { + return { + registerTips: (tips) => { + this.addedTips.add(tips); + this.backgroundTipsView.tipsChanged(); + return new Disposable(() => { + this.addedTips.delete(tips); + this.backgroundTipsView.tipsChanged(); + }); + }, + }; + }, +}; diff --git a/packages/background-tips/lib/tips.js b/packages/background-tips/lib/tips.js index fa70dc48a7..67d4619abf 100644 --- a/packages/background-tips/lib/tips.js +++ b/packages/background-tips/lib/tips.js @@ -1,14 +1,8 @@ module.exports = [ - 'Close panels like find and replace with {body>core:cancel}', - `Everything ${atom.branding.name} can do is in the Command Palette. See it by using {command-palette:toggle}`, - 'You can quickly open files with the Fuzzy Finder. Try it by using {fuzzy-finder:toggle-file-finder}', - 'You can toggle the Tree View with {tree-view:toggle}', - 'You can focus the Tree View with {tree-view:toggle-focus}', - 'You can toggle the Git tab with {github:toggle-git-tab}', - 'You can focus the Git tab with {github:toggle-git-tab-focus}', - 'You can toggle the GitHub tab with {github:toggle-github-tab}', - 'You can focus the GitHub tab with {github:toggle-github-tab-focus}', - 'You can split a pane with {pane:split-right-and-copy-active-item}', - 'You can jump to a method in the editor using {symbols-view:toggle-file-symbols}', - 'You can install packages and themes from the Settings View {settings-view:open}' -] + "Split your editor into multiple panes with {pane:split-right-and-copy-active-item}", + "You can move lines of text up and down with {editor:move-line-up} and {editor:move-line-down}", + "Toggle line comments quickly with {editor:toggle-line-comments}", + // Tips for deferred packages that won't register via services until activated + `The Command Palette lets you access all of ${atom.branding.name}'s commands. Open it with {command-palette:toggle}`, + 'Dismiss panels like Find and Replace with {body>core:cancel}', +]; diff --git a/packages/background-tips/package.json b/packages/background-tips/package.json index d3b7bb1145..736d581c0f 100644 --- a/packages/background-tips/package.json +++ b/packages/background-tips/package.json @@ -9,6 +9,14 @@ "engines": { "atom": ">0.42.0" }, + "providedServices": { + "background-tips": { + "description": "Allows packages to register tips shown when no editors are open", + "versions": { + "1.0.0": "provideBackgroundTips" + } + } + }, "dependencies": { "underscore-plus": "1.x" } diff --git a/packages/background-tips/spec/async-spec-helpers.js b/packages/background-tips/spec/async-spec-helpers.js index 73002c049a..c989e292c8 100644 --- a/packages/background-tips/spec/async-spec-helpers.js +++ b/packages/background-tips/spec/async-spec-helpers.js @@ -1,103 +1,106 @@ /** @babel */ -export function beforeEach (fn) { +export function beforeEach(fn) { global.beforeEach(function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) + }); } -export function afterEach (fn) { +export function afterEach(fn) { global.afterEach(function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) + }); } -['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { +["it", "fit", "ffit", "fffit"].forEach(function (name) { module.exports[name] = function (description, fn) { if (fn === undefined) { - global[name](description) - return + global[name](description); + return; } global[name](description, function () { - const result = fn() + const result = fn(); if (result instanceof Promise) { - waitsForPromise(() => result) + waitsForPromise(() => result); } - }) - } -}) + }); + }; +}); -export async function conditionPromise (condition, description = 'anonymous condition') { - const startTime = Date.now() +export async function conditionPromise( + condition, + description = "anonymous condition" +) { + const startTime = Date.now(); while (true) { - await timeoutPromise(100) + await timeoutPromise(100); if (await condition()) { - return + return; } if (Date.now() - startTime > 5000) { - throw new Error('Timed out waiting on ' + description) + throw new Error("Timed out waiting on " + description); } } } -export function timeoutPromise (timeout) { +export function timeoutPromise(timeout) { return new Promise(function (resolve) { - global.setTimeout(resolve, timeout) - }) + global.setTimeout(resolve, timeout); + }); } -function waitsForPromise (fn) { - const promise = fn() - global.waitsFor('spec promise to resolve', function (done) { +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() - }) - }) + jasmine.getEnv().currentSpec.fail(error); + done(); + }); + }); } -export function emitterEventPromise (emitter, event, timeout = 15000) { +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) + reject(new Error(`Timed out waiting for '${event}' event`)); + }, timeout); emitter.once(event, () => { - clearTimeout(timeoutHandle) - resolve() - }) - }) + clearTimeout(timeoutHandle); + resolve(); + }); + }); } -export function promisify (original) { +export function promisify(original) { return function (...args) { return new Promise((resolve, reject) => { args.push((err, ...results) => { if (err) { - reject(err) + reject(err); } else { - resolve(...results) + resolve(...results); } - }) + }); - return original(...args) - }) - } + return original(...args); + }); + }; } -export function promisifySome (obj, fnNames) { - const result = {} +export function promisifySome(obj, fnNames) { + const result = {}; for (const fnName of fnNames) { - result[fnName] = promisify(obj[fnName]) + result[fnName] = promisify(obj[fnName]); } - return result + return result; } diff --git a/packages/background-tips/spec/background-tips-spec.js b/packages/background-tips/spec/background-tips-spec.js index 86a3c78bfb..a967b6a0d3 100644 --- a/packages/background-tips/spec/background-tips-spec.js +++ b/packages/background-tips/spec/background-tips-spec.js @@ -1,142 +1,190 @@ -const {it, fit, ffit, afterEach, beforeEach, emitterEventPromise} = require('./async-spec-helpers') // eslint-disable-line no-unused-vars - -describe('BackgroundTips', () => { - let workspaceElement +const { + it, + fit, + ffit, + afterEach, + beforeEach, + emitterEventPromise, +} = require("./async-spec-helpers"); + +describe("BackgroundTips", () => { + let workspaceElement; const activatePackage = async () => { - const {mainModule} = await atom.packages.activatePackage('background-tips') - return mainModule.backgroundTipsView - } + const { mainModule } = await atom.packages.activatePackage( + "background-tips" + ); + return mainModule.backgroundTipsView; + }; beforeEach(() => { - workspaceElement = atom.views.getView(atom.workspace) - jasmine.attachToDOM(workspaceElement) - jasmine.useMockClock() - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(true) - }) + workspaceElement = atom.views.getView(atom.workspace); + jasmine.attachToDOM(workspaceElement); + jasmine.useMockClock(); + spyOn(atom.getCurrentWindow(), "isFocused").andReturn(true); + }); - describe('when the package is activated when there is only one pane', () => { + describe("when the package is activated when there is only one pane", () => { beforeEach(() => { - expect(atom.workspace.getCenter().getPanes().length).toBe(1) - }) - - describe('when the pane is empty', () => { - it('attaches the view after a delay', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) - - const backgroundTipsView = await activatePackage() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) - - describe('when the pane is not empty', () => { - it('does not attach the view', async () => { - await atom.workspace.open() - - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - }) - - describe('when a second pane is created', () => { - it('detaches the view', async () => { - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - - atom.workspace.getActivePane().splitRight() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - }) - }) - - describe('when the package is activated when there are multiple panes', () => { + expect(atom.workspace.getCenter().getPanes().length).toBe(1); + }); + + describe("when the pane is empty", () => { + it("attaches the view after a delay", async () => { + expect(atom.workspace.getActivePane().getItems().length).toBe(0); + + const backgroundTipsView = await activatePackage(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + }); + }); + + describe("when the pane is not empty", () => { + it("does not attach the view", async () => { + await atom.workspace.open(); + + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + }); + + describe("when a second pane is created", () => { + it("detaches the view", async () => { + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + + atom.workspace.getActivePane().splitRight(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + }); + }); + + describe("when the package is activated when there are multiple panes", () => { beforeEach(() => { - atom.workspace.getActivePane().splitRight() - expect(atom.workspace.getCenter().getPanes().length).toBe(2) - }) - - it('does not attach the view', async () => { - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) - - describe('when all but the last pane is destroyed', () => { - it('attaches the view', async () => { - const backgroundTipsView = await activatePackage() - atom.workspace.getActivePane().destroy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - - atom.workspace.getActivePane().splitRight() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - - atom.workspace.getActivePane().destroy() - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) - }) - - describe('when the view is attached', () => { - let backgroundTipsView + atom.workspace.getActivePane().splitRight(); + expect(atom.workspace.getCenter().getPanes().length).toBe(2); + }); + + it("does not attach the view", async () => { + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); + + describe("when all but the last pane is destroyed", () => { + it("attaches the view", async () => { + const backgroundTipsView = await activatePackage(); + atom.workspace.getActivePane().destroy(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + + atom.workspace.getActivePane().splitRight(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + + atom.workspace.getActivePane().destroy(); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + }); + }); + }); + + describe("when the view is attached", () => { + let backgroundTipsView; beforeEach(async () => { - expect(atom.workspace.getCenter().getPanes().length).toBe(1) - - backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay) - advanceClock(backgroundTipsView.fadeDuration) - }) - - it('has text in the message', () => { - expect(backgroundTipsView.element.parentNode).toBeTruthy() - expect(backgroundTipsView.message.textContent).toBeTruthy() - }) - - it('changes text in the message', async () => { - const oldText = backgroundTipsView.message.textContent - advanceClock(backgroundTipsView.displayDuration) - advanceClock(backgroundTipsView.fadeDuration) - expect(backgroundTipsView.message.textContent).not.toEqual(oldText) - }) - }) - - describe('when Atom is not focused but all other requirements are satisfied', () => { + expect(atom.workspace.getCenter().getPanes().length).toBe(1); + + backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay); + advanceClock(backgroundTipsView.fadeDuration); + }); + + it("has text in the message", () => { + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + expect(backgroundTipsView.message.textContent).toBeTruthy(); + }); + + it("changes text in the message", async () => { + const oldText = backgroundTipsView.message.textContent; + advanceClock(backgroundTipsView.displayDuration); + advanceClock(backgroundTipsView.fadeDuration); + expect(backgroundTipsView.message.textContent).not.toEqual(oldText); + }); + }); + + describe("provideBackgroundTips service", () => { + it("allows adding and removing tips", async () => { + const { mainModule } = await atom.packages.activatePackage( + "background-tips" + ); + const service = mainModule.provideBackgroundTips(); + + expect(typeof service.registerTips).toBe("function"); + + const originalCount = mainModule.getTips().length; + const disposable = service.registerTips(["Custom tip 1", "Custom tip 2"]); + expect(mainModule.getTips().length).toBe(originalCount + 2); + expect(mainModule.getTips()).toContain("Custom tip 1"); + expect(mainModule.getTips()).toContain("Custom tip 2"); + + disposable.dispose(); + expect(mainModule.getTips().length).toBe(originalCount); + expect(mainModule.getTips()).not.toContain("Custom tip 1"); + }); + + it("shows contributed tips when cycling", async () => { + const backgroundTipsView = await activatePackage(); + const { mainModule } = atom.packages.getActivePackage("background-tips"); + + const service = mainModule.provideBackgroundTips(); + service.registerTips(["Unique test tip for spec"]); + + advanceClock(backgroundTipsView.startDelay); + advanceClock(backgroundTipsView.fadeDuration); + + // The contributed tip should be in the rendered tips + expect( + backgroundTipsView.renderedTips.some((t) => + t.includes("Unique test tip for spec") + ) + ).toBe(true); + }); + }); + + describe("when Atom is not focused but all other requirements are satisfied", () => { beforeEach(() => { - jasmine.unspy(atom.getCurrentWindow(), 'isFocused') - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(false) - }) + jasmine.unspy(atom.getCurrentWindow(), "isFocused"); + spyOn(atom.getCurrentWindow(), "isFocused").andReturn(false); + }); - it('does not display the background tips', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) + it("does not display the background tips", async () => { + expect(atom.workspace.getActivePane().getItems().length).toBe(0); - const backgroundTipsView = await activatePackage() - expect(backgroundTipsView.element.parentNode).toBeFalsy() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() - }) + const backgroundTipsView = await activatePackage(); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); + }); - it('reactivates the background tips if the focus event is received', async () => { - expect(atom.workspace.getActivePane().getItems().length).toBe(0) + it("reactivates the background tips if the focus event is received", async () => { + expect(atom.workspace.getActivePane().getItems().length).toBe(0); - const backgroundTipsView = await activatePackage() - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeFalsy() + const backgroundTipsView = await activatePackage(); + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeFalsy(); - jasmine.unspy(atom.getCurrentWindow(), 'isFocused') - spyOn(atom.getCurrentWindow(), 'isFocused').andReturn(true) + jasmine.unspy(atom.getCurrentWindow(), "isFocused"); + spyOn(atom.getCurrentWindow(), "isFocused").andReturn(true); - const focusEvent = emitterEventPromise(atom.getCurrentWindow(), 'focus') - atom.getCurrentWindow().emit('focus') // Manually emit to prevent actually blurring + refocusing the window + const focusEvent = emitterEventPromise(atom.getCurrentWindow(), "focus"); + atom.getCurrentWindow().emit("focus"); // Manually emit to prevent actually blurring + refocusing the window - await focusEvent + await focusEvent; - advanceClock(backgroundTipsView.startDelay + 1) - expect(backgroundTipsView.element.parentNode).toBeTruthy() - }) - }) -}) + advanceClock(backgroundTipsView.startDelay + 1); + expect(backgroundTipsView.element.parentNode).toBeTruthy(); + }); + }); +}); diff --git a/packages/fuzzy-finder/lib/main.js b/packages/fuzzy-finder/lib/main.js index 1a5aa055d1..2bb1f7ba06 100644 --- a/packages/fuzzy-finder/lib/main.js +++ b/packages/fuzzy-finder/lib/main.js @@ -77,6 +77,14 @@ module.exports = { return new Disposable(() => metricsReporter.unsetReporter()) }, + consumeBackgroundTips (service) { + const disposable = service.registerTips([ + 'Quickly open any file in your project with the Fuzzy Finder using {fuzzy-finder:toggle-file-finder}' + ]) + this.disposables.add(disposable) + return disposable + }, + serialize () { const paths = {} for (let editor of atom.workspace.getTextEditors()) { diff --git a/packages/fuzzy-finder/package.json b/packages/fuzzy-finder/package.json index c928223a83..a7868ffdb7 100644 --- a/packages/fuzzy-finder/package.json +++ b/packages/fuzzy-finder/package.json @@ -41,6 +41,11 @@ "versions": { "^1.1.0": "consumeMetricsReporter" } + }, + "background-tips": { + "versions": { + "1.0.0": "consumeBackgroundTips" + } } }, "configSchema": { diff --git a/packages/settings-view/lib/main.js b/packages/settings-view/lib/main.js index 98781d3524..64438fd99b 100644 --- a/packages/settings-view/lib/main.js +++ b/packages/settings-view/lib/main.js @@ -58,6 +58,7 @@ module.exports = { }, deactivate () { + this.backgroundTipsDisposable?.dispose() if (settingsView) settingsView.destroy() if (statusView) statusView.destroy() settingsView = null @@ -84,6 +85,14 @@ module.exports = { } }, + consumeBackgroundTips (service) { + const disposable = service.registerTips([ + 'Install packages, themes, and customize your editor in Settings {settings-view:open}' + ]) + this.backgroundTipsDisposable = disposable + return disposable + }, + consumeSnippets (snippets) { if (typeof snippets.getUnparsedSnippets === 'function') { SnippetsProvider.getSnippets = snippets.getUnparsedSnippets.bind(snippets) diff --git a/packages/settings-view/package.json b/packages/settings-view/package.json index 6826a02fc3..9db5539c50 100644 --- a/packages/settings-view/package.json +++ b/packages/settings-view/package.json @@ -65,6 +65,11 @@ "versions": { "0.1.0": "consumeSnippets" } + }, + "background-tips": { + "versions": { + "1.0.0": "consumeBackgroundTips" + } } }, "deserializers": { diff --git a/packages/symbols-view/lib/main.js b/packages/symbols-view/lib/main.js index a8aedeed78..a61b3a2f89 100644 --- a/packages/symbols-view/lib/main.js +++ b/packages/symbols-view/lib/main.js @@ -111,10 +111,21 @@ module.exports = { this.broker?.destroy(); this.broker = null; + this.backgroundTipsDisposable?.dispose(); + this.backgroundTipsDisposable = null; + this.subscriptions?.dispose(); this.subscriptions = null; }, + consumeBackgroundTips (service) { + const disposable = service.registerTips([ + 'Navigate to any function or symbol in the current file with {symbols-view:toggle-file-symbols}' + ]) + this.backgroundTipsDisposable = disposable + return disposable + }, + consumeSymbolProvider(provider) { if (Array.isArray(provider)) { this.broker.add(...provider); diff --git a/packages/symbols-view/package.json b/packages/symbols-view/package.json index 8e10033008..ef4526d2fa 100644 --- a/packages/symbols-view/package.json +++ b/packages/symbols-view/package.json @@ -68,6 +68,11 @@ "versions": { "1.0.0": "consumeSymbolProvider" } + }, + "background-tips": { + "versions": { + "1.0.0": "consumeBackgroundTips" + } } }, "providedServices": { diff --git a/packages/tree-view/lib/tree-view-package.js b/packages/tree-view/lib/tree-view-package.js index 2b3e202c0f..910b959586 100644 --- a/packages/tree-view/lib/tree-view-package.js +++ b/packages/tree-view/lib/tree-view-package.js @@ -57,6 +57,15 @@ class TreeViewPackage { }) } + consumeBackgroundTips (service) { + const disposable = service.registerTips([ + 'Show or hide the Tree View with {tree-view:toggle}', + 'Jump to the Tree View without leaving your keyboard using {tree-view:toggle-focus}' + ]) + this.disposables.add(disposable) + return disposable + } + provideTreeView () { return { selectedPaths: () => this.getTreeViewInstance().selectedPaths(), diff --git a/packages/tree-view/package.json b/packages/tree-view/package.json index faef314eff..559c29dd18 100644 --- a/packages/tree-view/package.json +++ b/packages/tree-view/package.json @@ -29,6 +29,11 @@ "versions": { "1.0.0": "consumeElementIcons" } + }, + "background-tips": { + "versions": { + "1.0.0": "consumeBackgroundTips" + } } }, "providedServices": {