aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/repl/ammonite/filters/BasicFilters.scala
blob: faa97c348cdb1ea949f42d3dfc69ff86013f183b (plain) (tree)






















                                                                  
                    


                
                                                                                      
                                    
                                                                                        

   






































































                                                                                      
 



                                                                      
                                                    


                                                         
   

                                                  

                                                            
                                                












                                                      









                                 


                                                            
                                                          








                                                                 
 


                                                        
 



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

import ammonite.terminal.FilterTools._
import ammonite.terminal.LazyList._
import ammonite.terminal.SpecialKeys._
import ammonite.terminal.Filter
import ammonite.terminal._

/**
 * Filters for simple operation of a terminal: cursor-navigation
 * (including with all the modifier keys), enter/ctrl-c-exit, etc.
 */
object BasicFilters {
  def all = Filter.merge(
    navFilter,
    exitFilter,
    enterFilter,
    clearFilter,
    //loggingFilter,
    typingFilter
  )

  def injectNewLine(b: Vector[Char], c: Int, rest: LazyList[Int], indent: Int = 0) = {
    val (first, last) = b.splitAt(c)
    TermState(rest, (first :+ '\n') ++ last ++ Vector.fill(indent)(' '), c + 1 + indent)
  }

  def navFilter = Filter.merge(
    Case(Up)((b, c, m) => moveUp(b, c, m.width)),
    Case(Down)((b, c, m) => moveDown(b, c, m.width)),
    Case(Right)((b, c, m) => (b, c + 1)),
    Case(Left)((b, c, m) => (b, c - 1))
  )

  def tabColumn(indent: Int, b: Vector[Char], c: Int, rest: LazyList[Int]) = {
    val (chunks, chunkStarts, chunkIndex) = FilterTools.findChunks(b, c)
    val chunkCol = c - chunkStarts(chunkIndex)
    val spacesToInject = indent - (chunkCol % indent)
    val (lhs, rhs) = b.splitAt(c)
    TS(rest, lhs ++ Vector.fill(spacesToInject)(' ') ++ rhs, c + spacesToInject)
  }

  def tabFilter(indent: Int): Filter = Filter("tabFilter") {
    case TS(9 ~: rest, b, c, _) => tabColumn(indent, b, c, rest)
  }

  def loggingFilter: Filter = Filter("loggingFilter") {
    case TS(Ctrl('q') ~: rest, b, c, _) =>
      println("Char Display Mode Enabled! Ctrl-C to exit")
      var curr = rest
      while (curr.head != 3) {
        println("Char " + curr.head)
        curr = curr.tail
      }
      TS(curr, b, c)
  }

  def typingFilter: Filter = Filter("typingFilter") {
    case TS(p"\u001b[3~$rest", b, c, _) =>
//      Debug("fn-delete")
      val (first, last) = b.splitAt(c)
      TS(rest, first ++ last.drop(1), c)

    case TS(127 ~: rest, b, c, _) => // Backspace
      val (first, last) = b.splitAt(c)
      TS(rest, first.dropRight(1) ++ last, c - 1)

    case TS(char ~: rest, b, c, _) =>
//      Debug("NORMAL CHAR " + char)
      val (first, last) = b.splitAt(c)
      TS(rest, (first :+ char.toChar) ++ last, c + 1)
  }

  def doEnter(b: Vector[Char], c: Int, rest: LazyList[Int]) = {
    val (chunks, chunkStarts, chunkIndex) = FilterTools.findChunks(b, c)
    if (chunkIndex == chunks.length - 1) Result(b.mkString)
    else injectNewLine(b, c, rest)
  }

  def enterFilter: Filter = Filter("enterFilter") {
    case TS(13 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter
    case TS(10 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter
    case TS(10 ~: 13 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter
    case TS(13 ~: 10 ~: rest, b, c, _) => doEnter(b, c, rest) // Enter
  }

  def exitFilter: Filter = Filter("exitFilter") {
    case TS(Ctrl('c') ~: rest, b, c, _) =>
      Result("")
    case TS(Ctrl('d') ~: rest, b, c, _) =>
      // only exit if the line is empty, otherwise, behave like
      // "delete" (i.e. delete one char to the right)
      if (b.isEmpty) Exit else {
        val (first, last) = b.splitAt(c)
        TS(rest, first ++ last.drop(1), c)
      }
    case TS(-1 ~: rest, b, c, _) => Exit   // java.io.Reader.read() produces -1 on EOF
  }

  def clearFilter: Filter = Filter("clearFilter") {
    case TS(Ctrl('l') ~: rest, b, c, _) => ClearScreen(TS(rest, b, c))
  }

  def moveStart(b: Vector[Char], c: Int, w: Int) = {
    val (_, chunkStarts, chunkIndex) = findChunks(b, c)
    val currentColumn = (c - chunkStarts(chunkIndex)) % w
    b -> (c - currentColumn)
  }

  def moveEnd(b: Vector[Char], c: Int, w: Int) = {
    val (chunks, chunkStarts, chunkIndex) = findChunks(b, c)
    val currentColumn = (c - chunkStarts(chunkIndex)) % w
    val c1 = chunks.lift(chunkIndex + 1) match {
      case Some(next) =>
        val boundary = chunkStarts(chunkIndex + 1) - 1
        if ((boundary - c) > (w - currentColumn)) {
          val delta= w - currentColumn
          c + delta
        }
        else boundary
      case None =>
        c + 1 * 9999
    }
    b -> c1
  }

  def moveUpDown(
    b: Vector[Char],
    c: Int,
    w: Int,
    boundaryOffset: Int,
    nextChunkOffset: Int,
    checkRes: Int,
    check: (Int, Int) => Boolean,
    isDown: Boolean
  ) = {
    val (chunks, chunkStarts, chunkIndex) = findChunks(b, c)
    val offset = chunkStarts(chunkIndex + boundaryOffset)
    if (check(checkRes, offset)) checkRes
    else chunks.lift(chunkIndex + nextChunkOffset) match {
      case None => c + nextChunkOffset * 9999
      case Some(next) =>
        val boundary = chunkStarts(chunkIndex + boundaryOffset)
        val currentColumn = (c - chunkStarts(chunkIndex)) % w

        if (isDown) boundary + math.min(currentColumn, next)
        else boundary + math.min(currentColumn - next % w, 0) - 1
    }
  }

  def moveUp(b: Vector[Char], c: Int, w: Int) = {
    b -> moveUpDown(b, c, w, 0, -1, c - w, _ > _, false)
  }

  def moveDown(b: Vector[Char], c: Int, w: Int) = {
    b -> moveUpDown(b, c, w, 1, 1, c + w, _ <= _, true)
  }
}