Home Reference Source Repository

lib/decoration.js

'use babel'

import _ from 'underscore-plus'
import {Emitter} from 'atom'

var idCounter = 0
var nextId = function () { return idCounter++ }

/**
 * The `Decoration` class represents a decoration in the Minimap.
 *
 * It has the same API than the `Decoration` class of a text editor.
 */
export default class Decoration {

  /**
   * Returns `true` if the passed-in decoration properties matches the
   * specified type.
   *
   * @param  {Object} decorationProperties the decoration properties to match
   * @param  {string} type the decoration type to match
   * @return {boolean} whether the decoration properties match the type
   */
  static isType (decorationProperties, type) {
    if (_.isArray(decorationProperties.type)) {
      if (decorationProperties.type.indexOf(type) >= 0) { return true }
      return false
    } else {
      return type === decorationProperties.type
    }
  }

  /**
   * Creates a new decoration.
   *
   * @param  {Marker} marker the target marker for the decoration
   * @param  {Minimap} minimap the Minimap where the decoration will
   *                           be displayed
   * @param  {Object} properties the decoration's properties
   */
  constructor (marker, minimap, properties) {
    /**
     * @access private
     */
    this.marker = marker
    /**
     * @access private
     */
    this.minimap = minimap
    /**
     * @access private
     */
    this.emitter = new Emitter()
    /**
     * @access private
     */
    this.id = nextId()
    /**
     * @access private
     */
    this.properties = null
    this.setProperties(properties)
    this.properties.id = this.id
    /**
     * @access private
     */
    this.destroyed = false
    /**
     * @access private
     */
    this.markerDestroyDisposable = this.marker.onDidDestroy(() => {
      this.destroy()
    })
  }

  /**
   * Destroy this marker.
   *
   * If you own the marker, you should use `Marker#destroy` which will destroy
   * this decoration.
   */
  destroy () {
    if (this.destroyed) { return }

    this.markerDestroyDisposable.dispose()
    this.markerDestroyDisposable = null
    this.destroyed = true
    this.emitter.emit('did-destroy')
    this.emitter.dispose()
  }

  /**
   * Returns whether this decoration is destroyed or not.
   *
   * @return {boolean} whether this decoration is destroyed or not
   */
  isDestroyed () { return this.destroyed }

  /**
   * Registers an event listener to the `did-change-properties` event.
   *
   * This event is triggered when the decoration update method is called.
   *
   * @param  {function(change:Object):void} callback a function to call
   *                                        when the event is triggered
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidChangeProperties (callback) {
    return this.emitter.on('did-change-properties', callback)
  }

  /**
   * Registers an event listener to the `did-destroy` event.
   *
   * @param  {function():void} callback a function to call when the event
   *                                    is triggered
   * @return {Disposable} a disposable to stop listening to the event
   */
  onDidDestroy (callback) {
    return this.emitter.on('did-destroy', callback)
  }

  /**
   * An id unique across all Decoration objects.
   *
   * @return {number} the decoration id
   */
  getId () { return this.id }

  /**
   * Returns the marker associated with this Decoration.
   *
   * @return {Marker} the decoration's marker
   */
  getMarker () { return this.marker }

  /**
   * Check if this decoration is of type `type`.
   *
   * @param  {string|Array} type a type like `'line-number'`, `'line'`, etc.
   *                             `type` can also be an Array of Strings, where
   *                             it will return true if the decoration's type
   *                             matches any in the array.
   * @return {boolean} whether this decoration match the passed-in type
   */
  isType (type) {
    return Decoration.isType(this.properties, type)
  }

  /**
   * Returns the Decoration's properties.
   *
   * @return {Object} the decoration's properties
   */
  getProperties () {
    return this.properties
  }

  /**
   * Update the marker with new properties. Allows you to change the
   * decoration's class.
   *
   * @param {Object} newProperties the new properties for the decoration
   */
  setProperties (newProperties) {
    if (this.destroyed) { return }

    let oldProperties = this.properties
    this.properties = newProperties
    this.properties.id = this.id

    this.emitter.emit('did-change-properties', {oldProperties, newProperties})
  }
}