isac-java/src/java/isac/gui/mawen/syntax/isabelle/text.scala
author Walther Neuper <wneuper@ist.tugraz.at>
Wed, 28 Nov 2018 08:59:29 +0100
changeset 5232 34f18fdc3103
parent 5229 6bf0e95981e3
child 5239 b4e3883d7b66
permissions -rw-r--r--
update Scala 2.10 to 2.12.3
wneuper@5050
     1
/*  THIS IS A COPY OF ...
wneuper@5050
     2
    Title:      Pure/PIDE/text.scala
wneuper@5050
     3
    Author:     Fabian Immler, TU Munich
wneuper@5050
     4
    Author:     Makarius
wneuper@5050
     5
    ... WITH ADDITIONS AFTER "/---..---\"
wneuper@5050
     6
wneuper@5050
     7
Basic operations on plain text.
wneuper@5050
     8
*/
wneuper@5050
     9
wneuper@5050
    10
package isac.gui.mawen.syntax.isabelle
wneuper@5050
    11
wneuper@5050
    12
wneuper@5050
    13
import scala.collection.mutable
wneuper@5050
    14
import scala.util.Sorting
wneuper@5050
    15
wneuper@5229
    16
import info.hupel.isabelle.api.XML // + for Isabelle/Isac
wneuper@5050
    17
wneuper@5050
    18
wneuper@5050
    19
object Text
wneuper@5050
    20
{
wneuper@5050
    21
  /* offset */
wneuper@5050
    22
wneuper@5050
    23
  type Offset = Int
wneuper@5050
    24
wneuper@5050
    25
wneuper@5050
    26
  /* range -- with total quasi-ordering */
wneuper@5050
    27
wneuper@5050
    28
  object Range
wneuper@5050
    29
  {
wneuper@5050
    30
    def apply(start: Offset): Range = Range(start, start)
wneuper@5050
    31
wneuper@5050
    32
    val offside: Range = apply(-1)
wneuper@5050
    33
wneuper@5050
    34
    object Ordering extends scala.math.Ordering[Text.Range]
wneuper@5050
    35
    {
wneuper@5050
    36
      def compare(r1: Text.Range, r2: Text.Range): Int = r1 compare r2
wneuper@5050
    37
    }
wneuper@5050
    38
  }
wneuper@5050
    39
wneuper@5050
    40
  sealed case class Range(start: Offset, stop: Offset)
wneuper@5050
    41
  {
wneuper@5050
    42
    // denotation: {start} Un {i. start < i & i < stop}
wneuper@5050
    43
    if (start > stop)
wneuper@5232
    44
      Console.printf("Bad range: [" + start.toString + ":" + stop.toString + "]")
wneuper@5050
    45
wneuper@5050
    46
    override def toString: String = "[" + start.toString + ":" + stop.toString + "]"
wneuper@5050
    47
wneuper@5050
    48
    def length: Int = stop - start
wneuper@5050
    49
wneuper@5050
    50
    def map(f: Offset => Offset): Range = Range(f(start), f(stop))
wneuper@5050
    51
    def +(i: Offset): Range = if (i == 0) this else map(_ + i)
wneuper@5050
    52
    def -(i: Offset): Range = if (i == 0) this else map(_ - i)
wneuper@5050
    53
wneuper@5050
    54
    def is_singularity: Boolean = start == stop
wneuper@5050
    55
    def inflate_singularity: Range = if (is_singularity) Range(start, start + 1) else this
wneuper@5050
    56
wneuper@5050
    57
    def touches(i: Offset): Boolean = start <= i && i <= stop
wneuper@5050
    58
wneuper@5050
    59
    def contains(i: Offset): Boolean = start == i || start < i && i < stop
wneuper@5050
    60
    def contains(that: Range): Boolean = this.contains(that.start) && that.stop <= this.stop
wneuper@5050
    61
    def overlaps(that: Range): Boolean = this.contains(that.start) || that.contains(this.start)
wneuper@5050
    62
    def compare(that: Range): Int = if (overlaps(that)) 0 else this.start compare that.start
wneuper@5050
    63
wneuper@5050
    64
    def apart(that: Range): Boolean =
wneuper@5050
    65
      (this.start max that.start) > (this.stop min that.stop)
wneuper@5050
    66
wneuper@5050
    67
    def restrict(that: Range): Range =
wneuper@5050
    68
      Range(this.start max that.start, this.stop min that.stop)
wneuper@5050
    69
wneuper@5050
    70
    def try_restrict(that: Range): Option[Range] =
wneuper@5050
    71
      if (this apart that) None
wneuper@5050
    72
      else Some(restrict(that))
wneuper@5050
    73
wneuper@5050
    74
    def try_join(that: Range): Option[Range] =
wneuper@5050
    75
      if (this apart that) None
wneuper@5050
    76
      else Some(Range(this.start min that.start, this.stop max that.stop))
wneuper@5050
    77
  }
wneuper@5050
    78
wneuper@5050
    79
wneuper@5050
    80
  /* perspective */
wneuper@5050
    81
wneuper@5050
    82
  object Perspective
wneuper@5050
    83
  {
wneuper@5050
    84
    val empty: Perspective = Perspective(Nil)
wneuper@5050
    85
wneuper@5050
    86
    def full: Perspective = Perspective(List(Range(0, Integer.MAX_VALUE / 2)))
wneuper@5050
    87
wneuper@5050
    88
    def apply(ranges: Seq[Range]): Perspective =
wneuper@5050
    89
    {
wneuper@5050
    90
      val result = new mutable.ListBuffer[Text.Range]
wneuper@5050
    91
      var last: Option[Text.Range] = None
wneuper@5050
    92
      def ship(next: Option[Range]) { result ++= last; last = next }
wneuper@5050
    93
wneuper@5050
    94
      for (range <- ranges.sortBy(_.start))
wneuper@5050
    95
      {
wneuper@5050
    96
        last match {
wneuper@5050
    97
          case None => ship(Some(range))
wneuper@5050
    98
          case Some(last_range) =>
wneuper@5050
    99
            last_range.try_join(range) match {
wneuper@5050
   100
              case None => ship(Some(range))
wneuper@5050
   101
              case joined => last = joined
wneuper@5050
   102
            }
wneuper@5050
   103
        }
wneuper@5050
   104
      }
wneuper@5050
   105
      ship(None)
wneuper@5050
   106
      new Perspective(result.toList)
wneuper@5050
   107
    }
wneuper@5050
   108
  }
wneuper@5050
   109
wneuper@5050
   110
  final class Perspective private(
wneuper@5050
   111
    val ranges: List[Range]) // visible text partitioning in canonical order
wneuper@5050
   112
  {
wneuper@5050
   113
    def is_empty: Boolean = ranges.isEmpty
wneuper@5050
   114
    def range: Range =
wneuper@5050
   115
      if (is_empty) Range(0)
wneuper@5050
   116
      else Range(ranges.head.start, ranges.last.stop)
wneuper@5050
   117
wneuper@5050
   118
    override def hashCode: Int = ranges.hashCode
wneuper@5050
   119
    override def equals(that: Any): Boolean =
wneuper@5050
   120
      that match {
wneuper@5050
   121
        case other: Perspective => ranges == other.ranges
wneuper@5050
   122
        case _ => false
wneuper@5050
   123
      }
wneuper@5050
   124
    override def toString: String = ranges.toString
wneuper@5050
   125
  }
wneuper@5050
   126
wneuper@5050
   127
wneuper@5050
   128
  /* information associated with text range */
wneuper@5050
   129
wneuper@5050
   130
  sealed case class Info[A](range: Text.Range, info: A)
wneuper@5050
   131
  {
wneuper@5050
   132
    def restrict(r: Text.Range): Info[A] = Info(range.restrict(r), info)
wneuper@5050
   133
    def try_restrict(r: Text.Range): Option[Info[A]] = range.try_restrict(r).map(Info(_, info))
wneuper@5050
   134
  }
wneuper@5050
   135
wneuper@5050
   136
  type Markup = Info[XML.Elem]
wneuper@5050
   137
wneuper@5050
   138
wneuper@5050
   139
  /* editing */
wneuper@5050
   140
wneuper@5050
   141
  object Edit
wneuper@5050
   142
  {
wneuper@5050
   143
    def insert(start: Offset, text: String): Edit = new Edit(true, start, text)
wneuper@5050
   144
    def remove(start: Offset, text: String): Edit = new Edit(false, start, text)
wneuper@5050
   145
  }
wneuper@5050
   146
wneuper@5050
   147
  final class Edit private(val is_insert: Boolean, val start: Offset, val text: String)
wneuper@5050
   148
  {
wneuper@5050
   149
    override def toString: String =
wneuper@5050
   150
      (if (is_insert) "Insert(" else "Remove(") + (start, text).toString + ")"
wneuper@5050
   151
wneuper@5050
   152
wneuper@5050
   153
    /* transform offsets */
wneuper@5050
   154
wneuper@5050
   155
    private def transform(do_insert: Boolean, i: Offset): Offset =
wneuper@5050
   156
      if (i < start) i
wneuper@5050
   157
      else if (do_insert) i + text.length
wneuper@5050
   158
      else (i - text.length) max start
wneuper@5050
   159
wneuper@5050
   160
    def convert(i: Offset): Offset = transform(is_insert, i)
wneuper@5050
   161
    def revert(i: Offset): Offset = transform(!is_insert, i)
wneuper@5050
   162
wneuper@5050
   163
wneuper@5050
   164
    /* edit strings */
wneuper@5050
   165
wneuper@5050
   166
    private def insert(i: Offset, string: String): String =
wneuper@5050
   167
      string.substring(0, i) + text + string.substring(i)
wneuper@5050
   168
wneuper@5050
   169
    private def remove(i: Offset, count: Int, string: String): String =
wneuper@5050
   170
      string.substring(0, i) + string.substring(i + count)
wneuper@5050
   171
wneuper@5050
   172
    def can_edit(string: String, shift: Int): Boolean =
wneuper@5050
   173
      shift <= start && start < shift + string.length
wneuper@5050
   174
wneuper@5050
   175
    def edit(string: String, shift: Int): (Option[Edit], String) =
wneuper@5050
   176
      if (!can_edit(string, shift)) (Some(this), string)
wneuper@5050
   177
      else if (is_insert) (None, insert(start - shift, string))
wneuper@5050
   178
      else {
wneuper@5050
   179
        val i = start - shift
wneuper@5050
   180
        val count = text.length min (string.length - i)
wneuper@5050
   181
        val rest =
wneuper@5050
   182
          if (count == text.length) None
wneuper@5050
   183
          else Some(Edit.remove(start, text.substring(count)))
wneuper@5050
   184
        (rest, remove(i, count, string))
wneuper@5050
   185
      }
wneuper@5050
   186
  }
wneuper@5050
   187
  
wneuper@5050
   188
//----------------- ADDITIONS TO Isabelle2016-1 FOR ISAC -----------------\\
wneuper@5050
   189
  
wneuper@5050
   190
}