<commit_before>package za.jwatson.glycanoweb.react

import japgolly.scalajs.react.Addons.Perf
import japgolly.scalajs.react.MonocleReact._
import japgolly.scalajs.react.ScalazReact._
import japgolly.scalajs.react._
import japgolly.scalajs.react.extra._
import japgolly.scalajs.react.extra.Reusability._
import japgolly.scalajs.react.vdom.prefix_<^._
import monocle.Lens
import monocle.Monocle._
import monocle.macros.Lenses
import org.scalajs.dom
import org.scalajs.dom.EventTarget
import org.scalajs.dom.ext.LocalStorage
import org.scalajs.dom.raw.SVGRect
import za.jwatson.glycanoweb.Gly
import za.jwatson.glycanoweb.react.GlycanoCanvas.{Bounds, View}
import za.jwatson.glycanoweb.react.bootstrap._
import za.jwatson.glycanoweb.render.{DisplayConv, SubstituentShape}
import za.jwatson.glycanoweb.structure.RGraph._
import za.jwatson.glycanoweb.structure._

import scala.reflect.macros.whitebox
import scala.scalajs.js
import scala.util.{Failure, Success, Try}
import scalaz.State
import scalaz.effect.IO

object GlycanoApp {
  case class Props(conventions: Map[String, DisplayConv])

  type Selection = (Set[ResidueId], Set[AnnotId])

  object Props {
    implicit val reusability: Reusability[Props] = Reusability.by_==
  }

  @Lenses case class Snap(showGrid: Boolean = false,
                          gridWidth: Double = 10.0,
                          snapToGrid: Boolean = false,
                          snapRotation: Boolean = false,
                          snapRotationDegrees: Double = 15.0)

  object Snap {
    implicit val reusability = Reusability.by_==[Snap]
  }

  @Lenses case class AppState(undoPosition: Int = 0,
                              history: Vector[RGraph] = Vector(RGraph()),
                              selection: Selection = (Set.empty, Set.empty),
                              highlightBond: Option[ResidueId] = None,
                              placeAnomer: Anomer = Anomer.Alpha, placeAbsolute: Absolute = Absolute.D,
                              bondLabels: Boolean = false,
                              view: View = View(),
                              buffer: RGraph = RGraph(),
                              mode: Mode = Mode.Select,
                              displayConv: DisplayConv = DisplayConv.convUCT,
                              scaleSubstituents: Double = 1.0,
                              limitUpdateRate: Boolean = false,
                              annotationFontSize: Double = 24,
                              bounds: Option[Bounds] = None,
                              snap: Snap = Snap()

  ) {
    def graph: RGraph = history(undoPosition)

    def doCopy = {
      val copiedResidues = graph.residues.filterKeys(selection._1.contains)
      val copiedAnnotations = graph.annotations.filterKeys(selection._2.contains)
      this &|-> AppState.buffer set RGraph(copiedResidues, copiedAnnotations)
    }

    def doDelete = {
      val remainingResidues = graph.residues.filterKeys(r => !selection._1.contains(r))
      val remainingAnnotations = graph.annotations.filterKeys(a => !selection._2.contains(a))
      this &|-> AppStateL.graphL set RGraph(remainingResidues, remainingAnnotations)
    }

    def doCut = this.doCopy.doDelete

    private val offsetAmount = 20
    private val offsetBufferResidues = RGraph.residues ^|->> each modify {
      (GraphEntry.x modify (_ + offsetAmount)) andThen (GraphEntry.y modify (_ + offsetAmount))
    }
    private val offsetBufferAnnotations = RGraph.annotations ^|->> each modify {
      (Annot.x modify (_ + offsetAmount)) andThen (Annot.y modify (_ + offsetAmount))
    }
    private val offsetBuffer = offsetBufferResidues andThen offsetBufferAnnotations

    def doPaste = {
      val buf = offsetBuffer(buffer)
      val lookupR = buf.residues.map { case (id, _) => id -> ResidueId.next() }
      val lookupA = buf.annotations.map { case (id, _) => id -> AnnotId.next() }

      val modChildren = GraphEntry.children ^|->> each modify lookupR<commit_msg>only copy bonds between residues that are both copied<commit_after>package za.jwatson.glycanoweb.react

import japgolly.scalajs.react.Addons.Perf
import japgolly.scalajs.react.MonocleReact._
import japgolly.scalajs.react.ScalazReact._
import japgolly.scalajs.react._
import japgolly.scalajs.react.extra._
import japgolly.scalajs.react.extra.Reusability._
import japgolly.scalajs.react.vdom.prefix_<^._
import monocle.Lens
import monocle.Monocle._
import monocle.macros.Lenses
import org.scalajs.dom
import org.scalajs.dom.EventTarget
import org.scalajs.dom.ext.LocalStorage
import org.scalajs.dom.raw.SVGRect
import za.jwatson.glycanoweb.Gly
import za.jwatson.glycanoweb.react.GlycanoCanvas.{Bounds, View}
import za.jwatson.glycanoweb.react.bootstrap._
import za.jwatson.glycanoweb.render.{DisplayConv, SubstituentShape}
import za.jwatson.glycanoweb.structure.RGraph._
import za.jwatson.glycanoweb.structure._

import scala.reflect.macros.whitebox
import scala.scalajs.js
import scala.util.{Failure, Success, Try}
import scalaz.State
import scalaz.effect.IO

object GlycanoApp {
  case class Props(conventions: Map[String, DisplayConv])

  type Selection = (Set[ResidueId], Set[AnnotId])

  object Props {
    implicit val reusability: Reusability[Props] = Reusability.by_==
  }

  @Lenses case class Snap(showGrid: Boolean = false,
                          gridWidth: Double = 10.0,
                          snapToGrid: Boolean = false,
                          snapRotation: Boolean = false,
                          snapRotationDegrees: Double = 15.0)

  object Snap {
    implicit val reusability = Reusability.by_==[Snap]
  }

  @Lenses case class AppState(undoPosition: Int = 0,
                              history: Vector[RGraph] = Vector(RGraph()),
                              selection: Selection = (Set.empty, Set.empty),
                              highlightBond: Option[ResidueId] = None,
                              placeAnomer: Anomer = Anomer.Alpha, placeAbsolute: Absolute = Absolute.D,
                              bondLabels: Boolean = false,
                              view: View = View(),
                              buffer: RGraph = RGraph(),
                              mode: Mode = Mode.Select,
                              displayConv: DisplayConv = DisplayConv.convUCT,
                              scaleSubstituents: Double = 1.0,
                              limitUpdateRate: Boolean = false,
                              annotationFontSize: Double = 24,
                              bounds: Option[Bounds] = None,
                              snap: Snap = Snap()

  ) {
    def graph: RGraph = history(undoPosition)

    private def checkParent(copiedResidues: Map[ResidueId, GraphEntry]) = GraphEntry.parent.modify(_.filter(link => copiedResidues.contains(link.r)))
    private def checkChildren(copiedResidues: Map[ResidueId, GraphEntry]) = GraphEntry.children.modify(_.filter(entry => copiedResidues.contains(entry._2)))
    def doCopy = {
      val copiedResidues = graph.residues.filterKeys(selection._1.contains)
      val copiedAnnotations = graph.annotations.filterKeys(selection._2.contains)
      val filteredResidues = copiedResidues &|->> each modify (checkParent(copiedResidues) andThen checkChildren(copiedResidues))
      this &|-> AppState.buffer set RGraph(filteredResidues, copiedAnnotations)
    }

    def doDelete = {
      val remainingResidues = graph.residues.filterKeys(r => !selection._1.contains(r))
      val remainingAnnotations = graph.annotations.filterKeys(a => !selection._2.contains(a))
      this &|-> AppStateL.graphL set RGraph(remainingResidues, remainingAnnotations)
    }

    def doCut = this.doCopy.doDelete

    private val offsetAmount = 20
    private val offsetBufferResidues = RGraph.residues ^|->> each modify {
      (GraphEntry.x modify (_ + offsetAmount)) andThen (GraphEntry.y modify (_ + offsetAmount))
    }
    private val offsetBufferAnnotations = RGraph.annotations ^|->> each modify {
      (Annot.x modify (_ + offsetAmount)) andThen (Annot.y modify (_ + offsetAmount))
    }
    private val offsetBuffer = offsetBufferResidues andThen offsetBufferAnnotations

    def doPaste = {
      val buf = offsetBuffer(buffer)
      val lookupR = buf.residues.map { case (id, _) => id -> ResidueId.next() }
      val lookupA = buf.annotations.map { case (id, _) => id -> AnnotId.next() }

      val modChildren = GraphEntry.children ^|->> each modify lookupR
{'fraction_non_alphanumeric': 0.09662522202486679, 'fraction_numerical': 0.0041444641799881585, 'mean_word_length': 2.566722972972973, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7391179', 'n_tokens_mistral': 2561, 'n_tokens_neox': 2468, 'n_words': 521}