Home Reference Source Repository

lib/mixins/plugin-management.js

'use babel'

import Mixin from 'mixto'
import { CompositeDisposable } from 'atom'

/**
 * Provides methods to manage minimap plugins.
 * Minimap plugins are Atom packages that will augment the minimap.
 * They have a secondary activation cycle going on constrained by the minimap
 * package activation. A minimap plugin life cycle will generally look
 * like this:
 *
 * 1. The plugin module is activated by Atom through the `activate` method
 * 2. The plugin then register itself as a minimap plugin using `registerPlugin`
 * 3. The plugin is activated/deactivated according to the minimap settings.
 * 4. On the plugin module deactivation, the plugin must unregisters itself
 *    from the minimap using the `unregisterPlugin`.
 *
 * @access public
 */
export default class PluginManagement extends Mixin {
  /**
   * Returns the Minimap main module instance.
   *
   * @return {Main} The Minimap main module instance.
   */
  provideMinimapServiceV1 () { return this }

  /**
   * Initializes the properties for plugins' management.
   *
   * @access private
   */
  initializePlugins () {
    /**
     * The registered Minimap plugins stored using their name as key.
     *
     * @type {Object}
     * @access private
     */
    this.plugins = {}
    /**
     * The plugins' subscriptions stored using the plugin names as keys.
     *
     * @type {Object}
     * @access private
     */
    this.pluginsSubscriptions = {}
  }

  /**
   * Registers a minimap `plugin` with the given `name`.
   *
   * @param {string} name The identifying name of the plugin.
   *                      It will be used as activation settings name
   *                      as well as the key to unregister the module.
   * @param {MinimapPlugin} plugin The plugin to register.
   * @emits {did-add-plugin} with the name and a reference to the added plugin.
   * @emits {did-activate-plugin} if the plugin was activated during
   *                              the registration.
   */
  registerPlugin (name, plugin) {
    this.plugins[name] = plugin
    this.pluginsSubscriptions[name] = new CompositeDisposable()

    let event = { name: name, plugin: plugin }
    this.emitter.emit('did-add-plugin', event)

    if (atom.config.get('minimap.displayPluginsControls')) {
      this.registerPluginControls(name, plugin)
    }

    this.updatesPluginActivationState(name)
  }

  /**
   * Unregisters a plugin from the minimap.
   *
   * @param {string} name The identifying name of the plugin to unregister.
   * @emits {did-remove-plugin} with the name and a reference
   *        to the added plugin.
   */
  unregisterPlugin (name) {
    let plugin = this.plugins[name]

    if (atom.config.get('minimap.displayPluginsControls')) {
      this.unregisterPluginControls(name)
    }

    delete this.plugins[name]

    let event = { name: name, plugin: plugin }
    this.emitter.emit('did-remove-plugin', event)
  }

  /**
   * Toggles the specified plugin activation state.
   *
   * @param  {string} name     The name of the plugin.
   * @param  {boolean} boolean An optional boolean to set the activation
   *                           state of the plugin. If ommitted the new plugin
   *                           state will be the the inverse of its current
   *                           state.
   * @emits {did-activate-plugin} if the plugin was activated by the call.
   * @emits {did-deactivate-plugin} if the plugin was deactivated by the call.
   */
  togglePluginActivation (name, boolean) {
    let settingsKey = `minimap.plugins.${name}`

    if (boolean !== undefined && boolean !== null) {
      atom.config.set(settingsKey, boolean)
    } else {
      atom.config.set(settingsKey, !atom.config.get(settingsKey))
    }

    this.updatesPluginActivationState(name)
  }

  /**
   * Deactivates all the plugins registered in the minimap package so far.
   *
   * @emits {did-deactivate-plugin} for each plugin deactivated by the call.
   */
  deactivateAllPlugins () {
    for (let [name, plugin] of this.eachPlugin()) {
      plugin.deactivatePlugin()
      this.emitter.emit('did-deactivate-plugin', { name: name, plugin: plugin })
    }
  }

  /**
   * A generator function to iterate over registered plugins.
   *
   * @return An iterable that yield the name and reference to every plugin
   *         as an array in each iteration.
   */
  * eachPlugin () {
    for (let name in this.plugins) {
      yield [name, this.plugins[name]]
    }
  }

  /**
   * Updates the plugin activation state according to the current config.
   *
   * @param {string} name The identifying name of the plugin to update.
   * @emits {did-activate-plugin} if the plugin was activated by the call.
   * @emits {did-deactivate-plugin} if the plugin was deactivated by the call.
   * @access private
   */
  updatesPluginActivationState (name) {
    let plugin = this.plugins[name]
    let pluginActive = plugin.isActive()
    let settingActive = atom.config.get(`minimap.plugins.${name}`)
    let event = { name: name, plugin: plugin }

    if (settingActive && !pluginActive) {
      plugin.activatePlugin()
      this.emitter.emit('did-activate-plugin', event)
    } else if (pluginActive && !settingActive) {
      plugin.deactivatePlugin()
      this.emitter.emit('did-deactivate-plugin', event)
    }
  }

  /**
   * When the `minimap.displayPluginsControls` setting is toggled,
   * this function will register the commands and setting to manage the plugin
   * activation from the minimap settings.
   *
   * @param {string} name The identifying name of the plugin.
   * @param {MinimapPlugin} plugin The plugin instance to register
   *        controls for.
   * @listens {minimap.plugins.${name}} listen to the setting to update
   *          the plugin state accordingly.
   * @listens {minimap:toggle-${name}} listen to the command on `atom-workspace`
   *          to toggle the plugin state.
   * @access private
   */
  registerPluginControls (name, plugin) {
    let settingsKey = `minimap.plugins.${name}`

    this.config.plugins.properties[name] = {
      type: 'boolean',
      default: true
    }

    if (atom.config.get(settingsKey) === undefined) {
      atom.config.set(settingsKey, true)
    }

    this.pluginsSubscriptions[name].add(atom.config.observe(settingsKey, () => {
      this.updatesPluginActivationState(name)
    }))

    this.pluginsSubscriptions[name].add(atom.commands.add('atom-workspace', {
      [`minimap:toggle-${name}`]: () => {
        this.togglePluginActivation(name)
      }
    }))
  }

  /**
   * When the `minimap.displayPluginsControls` setting is toggled,
   * this function will unregister the commands and setting that
   * was created previously.
   *
   * @param {string} name The identifying name of the plugin.
   * @access private
   */
  unregisterPluginControls (name) {
    this.pluginsSubscriptions[name].dispose()
    delete this.pluginsSubscriptions[name]
    delete this.config.plugins.properties[name]
  }
}