Home Reference Source Repository

lib/main.js

'use babel'

import {Emitter, CompositeDisposable} from 'atom'
import include from './decorators/include'
import Minimap from './minimap'
import MinimapElement from './minimap-element'
import PluginManagement from './mixins/plugin-management'
import MinimapPluginGeneratorElement from './minimap-plugin-generator-element'

/**
 * The `Minimap` package provides an eagle-eye view of text buffers.
 *
 * It also provides API for plugin packages that want to interact with the
 * minimap and be available to the user through the minimap settings.
 */
@include(PluginManagement)
class Main {
  /**
   * Used only at export time.
   *
   * @access private
   */
  constructor () {
    /**
     * The activation state of the package.
     *
     * @type {boolean}
     * @access private
     */
    this.active = false
    /**
     * The toggle state of the package.
     *
     * @type {boolean}
     * @access private
     */
    this.toggled = false
    /**
     * The `Map` where Minimap instances are stored with the text editor they
     * target as key.
     *
     * @type {Map}
     * @access private
     */
    this.editorsMinimaps = null
    /**
     * The composite disposable that stores the package's subscriptions.
     *
     * @type {CompositeDisposable}
     * @access private
     */
    this.subscriptions = null
    /**
     * The disposable that stores the package's commands subscription.
     *
     * @type {Disposable}
     * @access private
     */
    this.subscriptionsOfCommands = null
    /**
     * The package's config object.
     *
     * @type {Object}
     * @access private
     */
    this.config = require('./config-schema.json')
    /**
     * The package's events emitter.
     *
     * @type {Emitter}
     * @access private
     */
    this.emitter = new Emitter()

    this.initializePlugins()
  }

  /**
   * Activates the minimap package.
   */
  activate () {
    if (this.active) { return }

    MinimapElement.registerViewProvider()

    this.subscriptionsOfCommands = atom.commands.add('atom-workspace', {
      'minimap:toggle': () => {
        this.toggle()
      },
      'minimap:generate-coffee-plugin': () => {
        this.generatePlugin('coffee')
      },
      'minimap:generate-javascript-plugin': () => {
        this.generatePlugin('javascript')
      },
      'minimap:generate-babel-plugin': () => {
        this.generatePlugin('babel')
      }
    })

    this.editorsMinimaps = new Map()
    this.subscriptions = new CompositeDisposable()
    this.active = true

    if (atom.config.get('minimap.autoToggle')) { this.toggle() }
  }

  /**
   * Deactivates the minimap package.
   */
  deactivate () {
    if (!this.active) { return }

    this.deactivateAllPlugins()

    if (this.editorsMinimaps) {
      this.editorsMinimaps.forEach((value, key) => {
        value.destroy()
        this.editorsMinimaps.delete(key)
      })
    }

    this.subscriptions.dispose()
    this.subscriptions = null
    this.subscriptionsOfCommands.dispose()
    this.subscriptionsOfCommands = null
    this.editorsMinimaps = undefined
    this.toggled = false
    this.active = false
  }

  /**
   * Toggles the minimap display.
   */
  toggle () {
    if (!this.active) { return }

    if (this.toggled) {
      this.toggled = false

      if (this.editorsMinimaps) {
        this.editorsMinimaps.forEach((value, key) => {
          value.destroy()
          this.editorsMinimaps.delete(key)
        })
      }
      this.subscriptions.dispose()
    } else {
      this.toggled = true
      this.initSubscriptions()
    }
  }

  /**
   * Opens the plugin generation view.
   *
   * @param  {string} template the name of the template to use
   */
  generatePlugin (template) {
    var view = new MinimapPluginGeneratorElement()
    view.template = template
    view.attach()
  }

  /**
   * Registers a callback to listen to the `did-activate` event of the package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidActivate (callback) {
    return this.emitter.on('did-activate', callback)
  }

  /**
   * Registers a callback to listen to the `did-deactivate` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidDeactivate (callback) {
    return this.emitter.on('did-deactivate', callback)
  }

  /**
   * Registers a callback to listen to the `did-create-minimap` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidCreateMinimap (callback) {
    return this.emitter.on('did-create-minimap', callback)
  }

  /**
   * Registers a callback to listen to the `did-add-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidAddPlugin (callback) {
    return this.emitter.on('did-add-plugin', callback)
  }

  /**
   * Registers a callback to listen to the `did-remove-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidRemovePlugin (callback) {
    return this.emitter.on('did-remove-plugin', callback)
  }

  /**
   * Registers a callback to listen to the `did-activate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidActivatePlugin (callback) {
    return this.emitter.on('did-activate-plugin', callback)
  }

  /**
   * Registers a callback to listen to the `did-deactivate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidDeactivatePlugin (callback) {
    return this.emitter.on('did-deactivate-plugin', callback)
  }

  /**
   * Returns the `Minimap` class
   *
   * @return {Function} the `Minimap` class constructor
   */
  minimapClass () { return Minimap }

  /**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditorElement`.
   *
   * @param  {TextEditorElement} editorElement a text editor element
   * @return {Minimap} the associated minimap
   */
  minimapForEditorElement (editorElement) {
    if (!editorElement) { return }
    return this.minimapForEditor(editorElement.getModel())
  }

  /**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor
   * @return {Minimap} the associated minimap
   */
  minimapForEditor (textEditor) {
    if (!textEditor) { return }

    let minimap = this.editorsMinimaps.get(textEditor)

    if (!minimap) {
      minimap = new Minimap({textEditor})
      this.editorsMinimaps.set(textEditor, minimap)

      var editorSubscription = textEditor.onDidDestroy(() => {
        let minimaps = this.editorsMinimaps
        if (minimaps) { minimaps.delete(textEditor) }
        editorSubscription.dispose()
      })
    }

    return minimap
  }

  /**
   * Returns a new stand-alone {Minimap} for the passed-in `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor instance to create
   *                                 a minimap for
   * @return {Minimap} a new stand-alone Minimap for the passed-in editor
   */
  standAloneMinimapForEditor (textEditor) {
    if (!textEditor) { return }

    return new Minimap({
      textEditor: textEditor,
      standAlone: true
    })
  }

  /**
   * Returns the `Minimap` associated to the active `TextEditor`.
   *
   * @return {Minimap} the active Minimap
   */
  getActiveMinimap () {
    return this.minimapForEditor(atom.workspace.getActiveTextEditor())
  }

  /**
   * Calls a function for each present and future minimaps.
   *
   * @param  {function(minimap:Minimap):void} iterator a function to call with
   *                                                   the existing and future
   *                                                   minimaps
   * @return {Disposable} a disposable to unregister the observer
   */
  observeMinimaps (iterator) {
    if (!iterator) { return }

    if (this.editorsMinimaps) {
      this.editorsMinimaps.forEach((minimap) => { iterator(minimap) })
    }
    return this.onDidCreateMinimap((minimap) => { iterator(minimap) })
  }

  /**
   * Registers to the `observeTextEditors` method.
   *
   * @access private
   */
  initSubscriptions () {
    this.subscriptions.add(atom.workspace.observeTextEditors((textEditor) => {
      let minimap = this.minimapForEditor(textEditor)
      let minimapElement = atom.views.getView(minimap)

      this.emitter.emit('did-create-minimap', minimap)

      minimapElement.attach()
    }))
  }
}

/**
 * The exposed instance of the `Main` class.
 *
 * @access private
 */
export default new Main()