| author | wenzelm | 
| Mon, 11 Mar 2019 20:47:04 +0100 | |
| changeset 69900 | 18a61caf5e68 | 
| parent 67014 | e6a695d6a6b2 | 
| child 70792 | ea2834adf8de | 
| permissions | -rw-r--r-- | 
| 64611 | 1 | /* Title: Pure/PIDE/line.scala | 
| 64605 | 2 | Author: Makarius | 
| 3 | ||
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 4 | Line-oriented text documents, with some bias towards VSCode. | 
| 64605 | 5 | */ | 
| 6 | ||
| 64611 | 7 | package isabelle | 
| 64605 | 8 | |
| 9 | ||
| 10 | import scala.annotation.tailrec | |
| 11 | ||
| 12 | ||
| 13 | object Line | |
| 14 | {
 | |
| 64806 | 15 | /* logical lines */ | 
| 16 | ||
| 17 | def normalize(text: String): String = | |
| 18 |     if (text.contains('\r')) text.replace("\r\n", "\n") else text
 | |
| 19 | ||
| 20 | def logical_lines(text: String): List[String] = | |
| 66923 | 21 | split_lines(normalize(text)) | 
| 64806 | 22 | |
| 23 | ||
| 64605 | 24 | /* position */ | 
| 25 | ||
| 26 | object Position | |
| 27 |   {
 | |
| 64650 | 28 | val zero: Position = Position() | 
| 66605 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 29 | val offside: Position = Position(line = -1) | 
| 65234 
1d6e9048cb62
normalize changes strictly as specified in the protocol definition (assuming non-overlapping ranges, amending 0f555ce33970), e.g. relevant for automatic quotes/parentheses around selection;
 wenzelm parents: 
65203diff
changeset | 30 | |
| 
1d6e9048cb62
normalize changes strictly as specified in the protocol definition (assuming non-overlapping ranges, amending 0f555ce33970), e.g. relevant for automatic quotes/parentheses around selection;
 wenzelm parents: 
65203diff
changeset | 31 | object Ordering extends scala.math.Ordering[Position] | 
| 
1d6e9048cb62
normalize changes strictly as specified in the protocol definition (assuming non-overlapping ranges, amending 0f555ce33970), e.g. relevant for automatic quotes/parentheses around selection;
 wenzelm parents: 
65203diff
changeset | 32 |     {
 | 
| 
1d6e9048cb62
normalize changes strictly as specified in the protocol definition (assuming non-overlapping ranges, amending 0f555ce33970), e.g. relevant for automatic quotes/parentheses around selection;
 wenzelm parents: 
65203diff
changeset | 33 | def compare(p1: Position, p2: Position): Int = p1 compare p2 | 
| 
1d6e9048cb62
normalize changes strictly as specified in the protocol definition (assuming non-overlapping ranges, amending 0f555ce33970), e.g. relevant for automatic quotes/parentheses around selection;
 wenzelm parents: 
65203diff
changeset | 34 | } | 
| 64605 | 35 | } | 
| 36 | ||
| 64650 | 37 | sealed case class Position(line: Int = 0, column: Int = 0) | 
| 64605 | 38 |   {
 | 
| 39 | def line1: Int = line + 1 | |
| 40 | def column1: Int = column + 1 | |
| 41 | def print: String = line1.toString + ":" + column1.toString | |
| 42 | ||
| 43 | def compare(that: Position): Int = | |
| 44 |       line compare that.line match {
 | |
| 45 | case 0 => column compare that.column | |
| 46 | case i => i | |
| 47 | } | |
| 48 | ||
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 49 | def advance(text: String): Position = | 
| 64617 | 50 | if (text.isEmpty) this | 
| 51 |       else {
 | |
| 64806 | 52 | val lines = logical_lines(text) | 
| 64619 | 53 | val l = line + lines.length - 1 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 54 | val c = (if (l == line) column else 0) + Library.trim_line(lines.last).length | 
| 64619 | 55 | Position(l, c) | 
| 64605 | 56 | } | 
| 57 | } | |
| 58 | ||
| 59 | ||
| 60 | /* range (right-open interval) */ | |
| 61 | ||
| 64647 | 62 | object Range | 
| 63 |   {
 | |
| 64666 | 64 | def apply(start: Position): Range = Range(start, start) | 
| 65 | val zero: Range = Range(Position.zero) | |
| 64647 | 66 | } | 
| 67 | ||
| 64605 | 68 | sealed case class Range(start: Position, stop: Position) | 
| 69 |   {
 | |
| 70 | if (start.compare(stop) > 0) | |
| 71 |       error("Bad line range: " + start.print + ".." + stop.print)
 | |
| 72 | ||
| 64647 | 73 | def print: String = | 
| 74 | if (start == stop) start.print | |
| 75 | else start.print + ".." + stop.print | |
| 64605 | 76 | } | 
| 77 | ||
| 78 | ||
| 64649 | 79 | /* positions within document node */ | 
| 80 | ||
| 66605 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 81 | object Node_Position | 
| 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 82 |   {
 | 
| 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 83 | def offside(name: String): Node_Position = Node_Position(name, Position.offside) | 
| 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 84 | } | 
| 
261dcd52c5a0
less aggressive default position: prefer persistent defaults maintained by jEdit (amending 89c5bb2a2128);
 wenzelm parents: 
65903diff
changeset | 85 | |
| 64651 | 86 | sealed case class Node_Position(name: String, pos: Position = Position.zero) | 
| 64650 | 87 |   {
 | 
| 88 | def line: Int = pos.line | |
| 89 | def column: Int = pos.column | |
| 90 | } | |
| 91 | ||
| 64651 | 92 | sealed case class Node_Range(name: String, range: Range = Range.zero) | 
| 64650 | 93 |   {
 | 
| 94 | def start: Position = range.start | |
| 95 | def stop: Position = range.stop | |
| 96 | } | |
| 64649 | 97 | |
| 98 | ||
| 64605 | 99 | /* document with newline as separator (not terminator) */ | 
| 100 | ||
| 101 | object Document | |
| 102 |   {
 | |
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 103 | def apply(text: String): Document = | 
| 65903 | 104 | Document(logical_lines(text).map(s => Line(Library.isolate_substring(s)))) | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 105 | |
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 106 |     val empty: Document = apply("")
 | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 107 | |
| 65159 | 108 | private def split(line_text: String): List[Line] = | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 109 | if (line_text == "") List(Line.empty) else apply(line_text).lines | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 110 | |
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 111 | private def chop(lines: List[Line]): (String, List[Line]) = | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 112 |       lines match {
 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 113 |         case Nil => ("", Nil)
 | 
| 65159 | 114 | case line :: rest => (line.text, rest) | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 115 | } | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 116 | |
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 117 | private def length(lines: List[Line]): Int = | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 118 | if (lines.isEmpty) 0 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 119 |       else ((0 /: lines) { case (n, line) => n + line.text.length + 1 }) - 1
 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 120 | |
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 121 |     def text(lines: List[Line]): String = lines.mkString("", "\n", "")
 | 
| 64605 | 122 | } | 
| 123 | ||
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 124 | sealed case class Document(lines: List[Line]) | 
| 64605 | 125 |   {
 | 
| 65197 | 126 | lazy val text_length: Text.Offset = Document.length(lines) | 
| 127 | def text_range: Text.Range = Text.Range(0, text_length) | |
| 128 | ||
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 129 | lazy val text: String = Document.text(lines) | 
| 64672 | 130 | |
| 67014 | 131 | def get_text(range: Text.Range): Option[String] = | 
| 65522 | 132 | if (text_range.contains(range)) Some(range.substring(text)) else None | 
| 64877 | 133 | |
| 64821 | 134 | override def toString: String = text | 
| 64605 | 135 | |
| 136 | override def equals(that: Any): Boolean = | |
| 137 |       that match {
 | |
| 138 | case other: Document => lines == other.lines | |
| 139 | case _ => false | |
| 140 | } | |
| 141 | override def hashCode(): Int = lines.hashCode | |
| 142 | ||
| 64683 
c0c09b6dfbe0
clarified signature: maintan Text.Length within Line.Document;
 wenzelm parents: 
64682diff
changeset | 143 | def position(text_offset: Text.Offset): Position = | 
| 64605 | 144 |     {
 | 
| 145 | @tailrec def move(i: Text.Offset, lines_count: Int, lines_rest: List[Line]): Position = | |
| 146 |       {
 | |
| 147 |         lines_rest match {
 | |
| 64650 | 148 | case Nil => require(i == 0); Position(lines_count) | 
| 64605 | 149 | case line :: ls => | 
| 150 | val n = line.text.length | |
| 64617 | 151 | if (ls.isEmpty || i <= n) | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 152 | Position(lines_count).advance(line.text.substring(n - i)) | 
| 64605 | 153 | else move(i - (n + 1), lines_count + 1, ls) | 
| 154 | } | |
| 155 | } | |
| 64681 
642b6105e6f4
clarified signature: explicit Length to avoid implicit mistakes;
 wenzelm parents: 
64679diff
changeset | 156 | move(text_offset, 0, lines) | 
| 64605 | 157 | } | 
| 158 | ||
| 64683 
c0c09b6dfbe0
clarified signature: maintan Text.Length within Line.Document;
 wenzelm parents: 
64682diff
changeset | 159 | def range(text_range: Text.Range): Range = | 
| 
c0c09b6dfbe0
clarified signature: maintan Text.Length within Line.Document;
 wenzelm parents: 
64682diff
changeset | 160 | Range(position(text_range.start), position(text_range.stop)) | 
| 64679 
b2bf280b7e13
more uniform treatment of input/output wrt. client;
 wenzelm parents: 
64672diff
changeset | 161 | |
| 64683 
c0c09b6dfbe0
clarified signature: maintan Text.Length within Line.Document;
 wenzelm parents: 
64682diff
changeset | 162 | def offset(pos: Position): Option[Text.Offset] = | 
| 64605 | 163 |     {
 | 
| 164 | val l = pos.line | |
| 165 | val c = pos.column | |
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 166 | val n = lines.length | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 167 |       if (0 <= l && l < n) {
 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 168 |         if (0 <= c && c <= lines(l).text.length) {
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 169 | val line_offset = | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 170 |             (0 /: lines.iterator.take(l)) { case (n, line) => n + line.text.length + 1 }
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 171 | Some(line_offset + c) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 172 | } | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 173 | else None | 
| 64605 | 174 | } | 
| 65197 | 175 | else if (l == n && c == 0) Some(text_length) | 
| 64605 | 176 | else None | 
| 177 | } | |
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 178 | |
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 179 | def change(remove: Range, insert: String): Option[(List[Text.Edit], Document)] = | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 180 |     {
 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 181 |       for {
 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 182 | edit_start <- offset(remove.start) | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 183 | if remove.stop == remove.start || offset(remove.stop).isDefined | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 184 | l1 = remove.start.line | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 185 | l2 = remove.stop.line | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 186 | if l1 <= l2 | 
| 65159 | 187 | (removed_text, new_lines) <- | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 188 |         {
 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 189 | val c1 = remove.start.column | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 190 | val c2 = remove.stop.column | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 191 | |
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 192 | val (prefix, lines1) = lines.splitAt(l1) | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 193 | val (s1, rest1) = Document.chop(lines1) | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 194 | |
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 195 |           if (l1 == l2) {
 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 196 |             if (0 <= c1 && c1 <= c2 && c2 <= s1.length) {
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 197 | Some( | 
| 65203 | 198 |                 if (lines1.isEmpty) ("", prefix ::: Document.split(insert))
 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 199 |                 else {
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 200 | val removed_text = s1.substring(c1, c2) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 201 | val changed_text = s1.substring(0, c1) + insert + s1.substring(c2) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 202 | (removed_text, prefix ::: Document.split(changed_text) ::: rest1) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 203 | }) | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 204 | } | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 205 | else None | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 206 | } | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 207 |           else {
 | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 208 | val (middle, lines2) = rest1.splitAt(l2 - l1 - 1) | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 209 | val (s2, rest2) = Document.chop(lines2) | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 210 |             if (0 <= c1 && c1 <= s1.length && 0 <= c2 && c2 <= s2.length) {
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 211 | Some( | 
| 65203 | 212 |                 if (lines1.isEmpty) ("", prefix ::: Document.split(insert))
 | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 213 |                 else {
 | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 214 | val r1 = s1.substring(c1) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 215 | val r2 = s2.substring(0, c2) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 216 | val removed_text = | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 217 | if (lines2.isEmpty) Document.text(Line(r1) :: middle) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 218 | else Document.text(Line(r1) :: middle ::: List(Line(r2))) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 219 | val changed_text = s1.substring(0, c1) + insert + s2.substring(c2) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 220 | (removed_text, prefix ::: Document.split(changed_text) ::: rest2) | 
| 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 221 | }) | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 222 | } | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 223 | else None | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 224 | } | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 225 | } | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 226 | } | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 227 | yield | 
| 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 228 | (Text.Edit.removes(edit_start, removed_text) ::: Text.Edit.inserts(edit_start, insert), | 
| 65196 
e8760a98db78
discontinued pointless Text.Length: Javascript and Java agree in old-fashioned UTF-16;
 wenzelm parents: 
65159diff
changeset | 229 | Document(new_lines)) | 
| 65157 
cd977a5bd928
clarified Document.offset: including final position;
 wenzelm parents: 
64877diff
changeset | 230 | } | 
| 64605 | 231 | } | 
| 232 | ||
| 233 | ||
| 234 | /* line text */ | |
| 235 | ||
| 236 |   val empty: Line = new Line("")
 | |
| 237 | def apply(text: String): Line = if (text == "") empty else new Line(text) | |
| 238 | } | |
| 239 | ||
| 240 | final class Line private(val text: String) | |
| 241 | {
 | |
| 242 | require(text.forall(c => c != '\r' && c != '\n')) | |
| 243 | ||
| 244 | override def equals(that: Any): Boolean = | |
| 245 |     that match {
 | |
| 246 | case other: Line => text == other.text | |
| 247 | case _ => false | |
| 248 | } | |
| 249 | override def hashCode(): Int = text.hashCode | |
| 250 | override def toString: String = text | |
| 251 | } |