wenzelm@38425: /* Title: Pure/PIDE/text.scala wenzelm@34276: Author: Fabian Immler, TU Munich wenzelm@34276: Author: Makarius wenzelm@34276: wenzelm@38425: Basic operations on plain text. wenzelm@34276: */ wenzelm@34276: wenzelm@34276: package isabelle wenzelm@34276: wenzelm@34276: wenzelm@38425: object Text wenzelm@34276: { wenzelm@38477: /* offset */ wenzelm@38426: wenzelm@38426: type Offset = Int wenzelm@38426: wenzelm@38477: wenzelm@38565: /* range -- with total quasi-ordering */ wenzelm@38477: wenzelm@38426: sealed case class Range(val start: Offset, val stop: Offset) wenzelm@38427: { wenzelm@38565: // denotation: {start} Un {i. start < i & i < stop} wenzelm@38477: require(start <= stop) wenzelm@38477: wenzelm@38563: override def toString = "[" + start.toString + ":" + stop.toString + "]" wenzelm@38563: wenzelm@38427: def map(f: Offset => Offset): Range = Range(f(start), f(stop)) wenzelm@38427: def +(i: Offset): Range = map(_ + i) wenzelm@38565: def contains(i: Offset): Boolean = start == i || start < i && i < stop wenzelm@38565: def contains(that: Range): Boolean = this.contains(that.start) && that.stop <= this.stop wenzelm@38565: def overlaps(that: Range): Boolean = this.contains(that.start) || that.contains(this.start) wenzelm@38565: def compare(that: Range): Int = if (overlaps(that)) 0 else this.start compare that.start wenzelm@38485: wenzelm@38485: def start_range: Range = Range(start, start) wenzelm@38485: def stop_range: Range = Range(stop, stop) wenzelm@38485: wenzelm@38564: def restrict(that: Range): Range = wenzelm@38485: Range(this.start max that.start, this.stop min that.stop) wenzelm@38427: } wenzelm@38426: wenzelm@38426: wenzelm@38426: /* editing */ wenzelm@34286: wenzelm@38425: object Edit wenzelm@38425: { wenzelm@38426: def insert(start: Offset, text: String): Edit = new Edit(true, start, text) wenzelm@38426: def remove(start: Offset, text: String): Edit = new Edit(false, start, text) wenzelm@38425: } wenzelm@34286: wenzelm@38426: class Edit(val is_insert: Boolean, val start: Offset, val text: String) wenzelm@38425: { wenzelm@38425: override def toString = wenzelm@38425: (if (is_insert) "Insert(" else "Remove(") + (start, text).toString + ")" wenzelm@34286: wenzelm@34286: wenzelm@38425: /* transform offsets */ wenzelm@34286: wenzelm@38426: private def transform(do_insert: Boolean, i: Offset): Offset = wenzelm@38426: if (i < start) i wenzelm@38426: else if (is_insert == do_insert) i + text.length wenzelm@38426: else (i - text.length) max start wenzelm@34286: wenzelm@38426: def convert(i: Offset): Offset = transform(true, i) wenzelm@38426: def revert(i: Offset): Offset = transform(false, i) wenzelm@38425: wenzelm@34286: wenzelm@38425: /* edit strings */ wenzelm@38425: wenzelm@38426: private def insert(i: Offset, string: String): String = wenzelm@38426: string.substring(0, i) + text + string.substring(i) wenzelm@34276: wenzelm@38426: private def remove(i: Offset, count: Int, string: String): String = wenzelm@38426: string.substring(0, i) + string.substring(i + count) wenzelm@38425: wenzelm@38425: def can_edit(string: String, shift: Int): Boolean = wenzelm@38425: shift <= start && start < shift + string.length wenzelm@38425: wenzelm@38425: def edit(string: String, shift: Int): (Option[Edit], String) = wenzelm@38425: if (!can_edit(string, shift)) (Some(this), string) wenzelm@38425: else if (is_insert) (None, insert(start - shift, string)) wenzelm@38425: else { wenzelm@38426: val i = start - shift wenzelm@38426: val count = text.length min (string.length - i) wenzelm@38425: val rest = wenzelm@38425: if (count == text.length) None wenzelm@38425: else Some(Edit.remove(start, text.substring(count))) wenzelm@38426: (rest, remove(i, count, string)) wenzelm@38425: } wenzelm@38425: } wenzelm@34276: }