aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/repl/ammonite/Filter.scala
blob: 9d34bb0f234b14400051da261df6bcfc765d59a8 (plain) (tree)
1
2
3
4
5
6
7
8





                         
                                                                           
                




                                                                   
                



                         
                                              
                                        
                                                                                   

                                                                 
 




































                                                                               
package dotty.tools
package dotc
package repl
package ammonite.terminal

object Filter {
  def apply(id: String)(f: PartialFunction[TermInfo, TermAction]): Filter =
    new Filter {
      val op = f.lift
      def identifier = id
    }

  def wrap(id: String)(f: TermInfo => Option[TermAction]): Filter =
    new Filter {
      val op = f
      def identifier = id
    }

  /** Merges multiple [[Filter]]s into one. */
  def merge(pfs: Filter*) = new Filter {
    val op = (v1: TermInfo) => pfs.iterator.map(_.op(v1)).find(_.isDefined).flatten
    def identifier = pfs.iterator.map(_.identifier).mkString(":")
  }

  val empty = Filter.merge()
}

/**
  * The way you configure your terminal behavior; a trivial wrapper around a
  * function, though you should provide a good `.toString` method to make
  * debugging easier. The [[TermInfo]] and [[TermAction]] types are its
  * interface to the terminal.
  *
  * [[Filter]]s are composed sequentially: if a filter returns `None` the next
  * filter is tried, while if a filter returns `Some` that ends the cascade.
  * While your `op` function interacts with the terminal purely through
  * immutable case classes, the Filter itself is free to maintain its own state
  * and mutate it whenever, even when returning `None` to continue the cascade.
  */
trait Filter {
  val op: TermInfo => Option[TermAction]

  /**
    * the `.toString` of this object, except by making it separate we force
    * the implementer to provide something and stop them from accidentally
    * leaving it as the meaningless default.
    */
  def identifier: String
  override def toString = identifier
}

/**
  * A filter as an abstract class, letting you provide a [[filter]] instead of
  * an `op`, automatically providing a good `.toString` for debugging, and
  * providing a reasonable "place" inside the inheriting class/object to put
  * state or helpers or other logic associated with the filter.
  */
abstract class DelegateFilter() extends Filter {
  def filter: Filter
  val op = filter.op
}