All Downloads are FREE. Search and download functionalities are using the official Maven repository.

polynote.kernel.data.scala Maven / Gradle / Ivy

The newest version!
package polynote.kernel

import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
import io.circe.{Decoder, Encoder}
import polynote.messages.{CellID, ShortString, TinyList, TinyMap, TinyString}
import polynote.runtime.CellRange
import scodec.codecs.{Discriminated, Discriminator, byte}
import scodec.{Attempt, Codec, Err}
import scodec.codecs.implicits._
import shapeless.cachedImplicit
import zio.Cause

import scala.reflect.internal.util.Position
import scala.util.Try

// TODO: should just make a codec for Position? To avoid extra object?
// TODO: can't recover line number from just this info. Should we store that?
final case class Pos(sourceId: String, start: Int, end: Int, point: Int) {
  def this(position: Position) = this(
    position.source.toString,
    if (position.isDefined) Try(position.start).getOrElse(-1) else -1,
    if (position.isDefined) Try(position.end).getOrElse(-1) else -1,
    position.pointOrElse(-1)
  )
}

object Pos {
  implicit val encoder: Encoder[Pos] = deriveEncoder[Pos]
  implicit val decoder: Decoder[Pos] = deriveDecoder[Pos]
}

final case class KernelReport(pos: Pos, msg: String, severity: Int) {
  def severityString: String = severity match {
    case KernelReport.Info => "Info"
    case KernelReport.Warning => "Warning"
    case _ => "Error"
  }

  override def toString: String =
    s"""$severityString: $msg (${pos.start})""" // TODO: line and column instead
}

object KernelReport {
  final val Info = 0
  final val Warning = 1
  final val Error = 2
  implicit val encoder: Encoder[KernelReport] = deriveEncoder[KernelReport]
  implicit val decoder: Decoder[KernelReport] = deriveDecoder[KernelReport]
}

sealed trait CompletionType

object CompletionType {
  final case object Term extends CompletionType
  final case object Field extends CompletionType
  final case object Method extends CompletionType
  final case object Package extends CompletionType
  final case object TraitType extends CompletionType
  final case object ClassType extends CompletionType
  final case object Module extends CompletionType
  final case object TypeAlias extends CompletionType
  final case object Keyword extends CompletionType
  final case object Unknown extends CompletionType

  // these were chosen to line up with LSP, for no reason other than convenience with Monaco
  val fromByte: PartialFunction[Byte, CompletionType] = {
    case 0  => Unknown
    case 5  => Term
    case 4  => Field
    case 1  => Method
    case 18 => Package
    case 7  => TraitType
    case 6  => ClassType
    case 8  => Module
    case 17 => TypeAlias
    case 13 => Keyword
  }

  def toByte(typ: CompletionType): Byte = typ match {
    case Unknown   => 0
    case Term      => 5
    case Field     => 4
    case Method    => 1
    case Package   => 18
    case TraitType => 7
    case ClassType => 6
    case Module    => 8
    case TypeAlias => 17
    case Keyword   => 13
  }

  implicit val codec: Codec[CompletionType] = scodec.codecs.byte.exmap(
    fromByte.lift andThen (opt => Attempt.fromOption[CompletionType](opt, Err("Invalid completion type number"))),
    toByte _ andThen Attempt.successful
  )
}

final case class Completion(
  name: TinyString,
  typeParams: TinyList[TinyString],
  paramLists: TinyList[TinyList[(TinyString, ShortString)]],
  resultType: ShortString,
  completionType: CompletionType,
  insertText: Option[ShortString] = None)

final case class ParameterHint(
  name: TinyString,
  typeName: TinyString,
  docString: Option[ShortString]) {
  override def toString: String = if (typeName.nonEmpty) s"$name: $typeName" else name
}

final case class ParameterHints(
  name: TinyString,
  docString: Option[ShortString],
  parameters: TinyList[ParameterHint])

final case class Signatures(
  hints: TinyList[ParameterHints],
  activeSignature: Byte,
  activeParameter: Byte)

sealed trait KernelStatusUpdate {
  def isRelevant(subscriber: Int): Boolean
  def forSubscriber(subscriber: Int): KernelStatusUpdate = this
}

sealed trait AlwaysRelevant { self: KernelStatusUpdate =>
  override def isRelevant(subscriber: Int): Boolean = true
}

object KernelStatusUpdate {
  implicit val discriminated: Discriminated[KernelStatusUpdate, Byte] = Discriminated(byte)
  implicit val codec: Codec[KernelStatusUpdate] = cachedImplicit
}

abstract class KernelStatusUpdateCompanion[T <: KernelStatusUpdate](id: Byte) {
  implicit val discriminator: Discriminator[KernelStatusUpdate, T, Byte] = Discriminator(id)
}

final case class SymbolInfo(
  name: TinyString,
  typeName: TinyString,
  valueText: TinyString,
  availableViews: TinyList[TinyString])

final case class UpdatedSymbols(
  newOrUpdated: TinyList[SymbolInfo],
  removed: TinyList[TinyString]
) extends KernelStatusUpdate with AlwaysRelevant

object UpdatedSymbols extends KernelStatusUpdateCompanion[UpdatedSymbols](0)

sealed trait TaskStatus {
  def isDone: Boolean
}

sealed trait DoneStatus extends TaskStatus { final val isDone: Boolean = true }
sealed trait NotDoneStatus extends TaskStatus { final val isDone: Boolean = false }

case object Complete extends DoneStatus
case object Queued extends NotDoneStatus
case object Running extends NotDoneStatus
case object ErrorStatus extends DoneStatus

object TaskStatus {


  val fromByte: PartialFunction[Byte, TaskStatus] = {
    case 0 => Complete
    case 1 => Running
    case 2 => Queued
    case 3 => ErrorStatus
  }

  def toByte(taskStatus: TaskStatus): Byte = taskStatus match {
    case Complete => 0
    case Running => 1
    case Queued => 2
    case ErrorStatus => 3
  }

  implicit val codec: Codec[TaskStatus] = byte.exmap(
    fromByte.lift andThen (Attempt.fromOption(_, Err("Invalid task status byte"))),
    s => Attempt.successful(toByte(s))
  )

  implicit val ordering: Ordering[TaskStatus] = new Ordering[TaskStatus] {
    // this isn't the most concise comparison routine, but it should give compiler warnings if any statuses are added w/o being handled
    def compare(x: TaskStatus, y: TaskStatus): Int = (x, y) match {
      case (Complete, Complete) | (Running, Running) | (Queued, Queued) | (ErrorStatus, ErrorStatus) => 0
      case (Complete, _) => 1
      case (_, Complete) => -1
      case (Queued, _)   => -1
      case (_, Queued)   => 1
      case (ErrorStatus, _)    => 1
      case (_, ErrorStatus)    => -1
    }
  }
}

final case class TaskInfo(
  id: TinyString,
  label: TinyString,
  detail: ShortString,
  status: TaskStatus,
  progress: Byte = 0,
  parent: Option[TinyString] = None) {

  def running: TaskInfo = copy(status = Running)
  def completed: TaskInfo = copy(status = Complete, progress = 255.toByte)
  def failed: TaskInfo = if (status == Complete) this else copy(status = ErrorStatus, progress = 255.toByte)
  def failed(err: Cause[Throwable]): TaskInfo = if (status == Complete) this else {
    val errMsg = Option(err.squash.getMessage).getOrElse(err.squash.toString)
    copy(status = ErrorStatus, detail = ShortString.truncate(errMsg), progress = 255.toByte)
  }
  def done(status: DoneStatus): TaskInfo = if (this.status.isDone) this else copy(status = status, progress = 255.toByte)
  def progress(fraction: Double): TaskInfo = copy(progress = (fraction * 255).toByte)
  def progress(fraction: Double, detailOpt: Option[String]): TaskInfo = copy(progress = (fraction * 255).toByte, detail = detailOpt.getOrElse(detail))
  def progressFraction: Double = progress.toDouble / 255
}

object TaskInfo {
  def apply(id: String): TaskInfo = TaskInfo(id, "", "", Queued)
}

final case class UpdatedTasks(
  tasks: TinyList[TaskInfo]
) extends KernelStatusUpdate with AlwaysRelevant

object UpdatedTasks extends KernelStatusUpdateCompanion[UpdatedTasks](1) {
  def one(info: TaskInfo): UpdatedTasks = UpdatedTasks(List(info))
}

final case class KernelBusyState(busy: Boolean, alive: Boolean) extends KernelStatusUpdate with AlwaysRelevant {
  def setBusy: KernelBusyState = copy(busy = true)
  def setIdle: KernelBusyState = copy(busy = false)
  def setAlive: KernelBusyState = copy(alive = true)
  def setDead: KernelBusyState = copy(alive = false)
}
object KernelBusyState extends KernelStatusUpdateCompanion[KernelBusyState](2)

//                                           key          html
final case class KernelInfo(content: TinyMap[ShortString, String]) extends KernelStatusUpdate with AlwaysRelevant {
  def combine(other: KernelInfo): KernelInfo = {
    copy(TinyMap(content ++ other.content))
  }

  def +(kv: (String, String)): KernelInfo = copy(content = TinyMap(content + (ShortString(kv._1) -> kv._2)))
}
object KernelInfo extends KernelStatusUpdateCompanion[KernelInfo](3) {
  def apply(tups: (String, String)*): KernelInfo = KernelInfo(TinyMap(tups.map {
    case (k, v) => ShortString(k) -> v
  }.toMap))
}

final case class ExecutionStatus(cellID: CellID, pos: Option[CellRange]) extends KernelStatusUpdate with AlwaysRelevant
object ExecutionStatus extends KernelStatusUpdateCompanion[ExecutionStatus](4)

final case class Presence(id: Int, name: TinyString, avatar: Option[ShortString])
final case class PresenceUpdate(added: TinyList[Presence], removed: TinyList[Int]) extends KernelStatusUpdate with AlwaysRelevant {
  override def forSubscriber(subscriber: Int): KernelStatusUpdate =
    copy(added = added.filterNot(_.id == subscriber), removed = removed.filterNot(_ == subscriber))
}
object PresenceUpdate extends KernelStatusUpdateCompanion[PresenceUpdate](5)

final case class PresenceSelection(presenceId: Int, cellID: CellID, range: CellRange) extends KernelStatusUpdate {
  override def isRelevant(subscriber: Int): Boolean = presenceId != subscriber
}
object PresenceSelection extends KernelStatusUpdateCompanion[PresenceSelection](6)

final case class KernelError(err: Throwable) extends KernelStatusUpdate with AlwaysRelevant
object KernelError extends KernelStatusUpdateCompanion[KernelError](7) {
  implicit val codec: Codec[KernelError] = RuntimeError.throwableWithCausesCodec.xmap(new KernelError(_), _.err)
}

final case class CellStatusUpdate(cellID: CellID, status: TaskStatus) extends KernelStatusUpdate with AlwaysRelevant
object CellStatusUpdate extends KernelStatusUpdateCompanion[CellStatusUpdate](8)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy