src/Tools/jEdit/src/proofdocument/ProofDocument.scala
author immler@in.tum.de
Wed Jul 08 15:15:15 2009 +0200 (2009-07-08)
changeset 34653 2e033aaf128e
parent 34649 70759ca6bb87
child 34655 77722b866fb4
permissions -rw-r--r--
commands carrying state-information
wenzelm@34407
     1
/*
wenzelm@34485
     2
 * Document as list of commands, consisting of lists of tokens
wenzelm@34407
     3
 *
wenzelm@34407
     4
 * @author Johannes Hölzl, TU Munich
immler@34532
     5
 * @author Fabian Immler, TU Munich
wenzelm@34485
     6
 * @author Makarius
wenzelm@34407
     7
 */
wenzelm@34407
     8
wenzelm@34318
     9
package isabelle.proofdocument
wenzelm@34318
    10
immler@34526
    11
import scala.collection.mutable.ListBuffer
immler@34532
    12
import scala.actors.Actor
immler@34532
    13
import scala.actors.Actor._
wenzelm@34318
    14
import java.util.regex.Pattern
wenzelm@34485
    15
import isabelle.prover.{Prover, Command}
immler@34531
    16
import isabelle.utils.LinearSet
wenzelm@34318
    17
wenzelm@34494
    18
case class StructureChange(
immler@34526
    19
  val before_change: Option[Command],
wenzelm@34494
    20
  val added_commands: List[Command],
wenzelm@34494
    21
  val removed_commands: List[Command])
wenzelm@34485
    22
wenzelm@34483
    23
object ProofDocument
wenzelm@34483
    24
{
wenzelm@34582
    25
  // Be careful when changing this regex. Not only must it handle the
wenzelm@34318
    26
  // spurious end of a token but also:  
wenzelm@34318
    27
  // Bug ID: 5050507 Pattern.matches throws StackOverflow Error
wenzelm@34318
    28
  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507
wenzelm@34318
    29
  
wenzelm@34483
    30
  val token_pattern = 
wenzelm@34318
    31
    Pattern.compile(
wenzelm@34318
    32
      "\\{\\*([^*]|\\*[^}]|\\*\\z)*(\\z|\\*\\})|" +
wenzelm@34318
    33
      "\\(\\*([^*]|\\*[^)]|\\*\\z)*(\\z|\\*\\))|" +
wenzelm@34318
    34
      "(\\?'?|')[A-Za-z_0-9.]*|" + 
wenzelm@34318
    35
      "[A-Za-z_0-9.]+|" + 
wenzelm@34318
    36
      "[!#$%&*+-/<=>?@^_|~]+|" +
wenzelm@34318
    37
      "\"([^\\\\\"]?(\\\\(.|\\z))?)*+(\"|\\z)|" +
wenzelm@34318
    38
      "`([^\\\\`]?(\\\\(.|\\z))?)*+(`|\\z)|" +
wenzelm@34318
    39
      "[()\\[\\]{}:;]", Pattern.MULTILINE)
wenzelm@34485
    40
immler@34544
    41
 val empty =
wenzelm@34603
    42
  new ProofDocument(isabelle.jedit.Isabelle.system.id(),
immler@34653
    43
    LinearSet(), Map(), LinearSet(), Map(), _ => false)
immler@34538
    44
wenzelm@34318
    45
}
wenzelm@34318
    46
wenzelm@34582
    47
class ProofDocument(
wenzelm@34582
    48
  val id: String,
wenzelm@34582
    49
  val tokens: LinearSet[Token],
wenzelm@34582
    50
  val token_start: Map[Token, Int],
wenzelm@34582
    51
  val commands: LinearSet[Command],
immler@34653
    52
  var states: Map[Command, IsarDocument.State_ID],
wenzelm@34582
    53
  is_command_keyword: String => Boolean)
wenzelm@34483
    54
{
wenzelm@34485
    55
immler@34653
    56
immler@34532
    57
  def set_command_keyword(f: String => Boolean): ProofDocument =
immler@34653
    58
    new ProofDocument(id, tokens, token_start, commands, states, f)
wenzelm@34485
    59
wenzelm@34582
    60
  def content = Token.string_from_tokens(Nil ++ tokens, token_start)
wenzelm@34485
    61
  /** token view **/
wenzelm@34485
    62
immler@34538
    63
  def text_changed(change: Text.Change): (ProofDocument, StructureChange) =
wenzelm@34485
    64
  {
immler@34551
    65
    //indices of tokens
immler@34551
    66
    var start: Map[Token, Int] = token_start
immler@34551
    67
    def stop(t: Token) = start(t) + t.length
immler@34551
    68
    // split old token lists
wenzelm@34582
    69
    val tokens = Nil ++ this.tokens
immler@34551
    70
    val (begin, remaining) = tokens.span(stop(_) < change.start)
immler@34648
    71
    val (removed, end) = remaining.span(token_start(_) <= change.start + change.removed.length)
immler@34551
    72
    // update indices
wenzelm@34582
    73
    start = end.foldLeft(start)((s, t) =>
immler@34648
    74
      s + (t -> (s(t) + change.added.length - change.removed.length)))
wenzelm@34485
    75
immler@34551
    76
    val split_begin = removed.takeWhile(start(_) < change.start).
immler@34554
    77
      map (t => {
immler@34554
    78
          val split_tok = new Token(t.content.substring(0, change.start - start(t)), t.kind)
immler@34554
    79
          start += (split_tok -> start(t))
immler@34554
    80
          split_tok
immler@34554
    81
        })
immler@34554
    82
immler@34648
    83
    val split_end = removed.dropWhile(stop(_) < change.start + change.removed.length).
immler@34554
    84
      map (t => {
wenzelm@34582
    85
          val split_tok =
immler@34648
    86
            new Token(t.content.substring(change.start + change.removed.length - start(t)), t.kind)
immler@34554
    87
          start += (split_tok -> start(t))
immler@34554
    88
          split_tok
immler@34554
    89
        })
immler@34551
    90
    // update indices
immler@34554
    91
    start = removed.foldLeft (start) ((s, t) => s - t)
immler@34554
    92
    start = split_end.foldLeft (start) ((s, t) =>
immler@34554
    93
    s + (t -> (change.start + change.added.length)))
wenzelm@34318
    94
immler@34551
    95
    val ins = new Token(change.added, Token.Kind.OTHER)
immler@34551
    96
    start += (ins -> change.start)
immler@34551
    97
    
wenzelm@34582
    98
    var invalid_tokens = split_begin ::: ins :: split_end ::: end
wenzelm@34582
    99
    var new_tokens: List[Token] = Nil
wenzelm@34582
   100
    var old_suffix: List[Token] = Nil
wenzelm@34318
   101
immler@34551
   102
    val match_start = invalid_tokens.firstOption.map(start(_)).getOrElse(0)
wenzelm@34582
   103
    val matcher =
wenzelm@34582
   104
      ProofDocument.token_pattern.matcher(Token.string_from_tokens(invalid_tokens, start))
immler@34526
   105
immler@34526
   106
    while (matcher.find() && invalid_tokens != Nil) {
wenzelm@34485
   107
			val kind =
wenzelm@34505
   108
        if (is_command_keyword(matcher.group))
wenzelm@34485
   109
          Token.Kind.COMMAND_START
wenzelm@34494
   110
        else if (matcher.end - matcher.start > 2 && matcher.group.substring(0, 2) == "(*")
wenzelm@34485
   111
          Token.Kind.COMMENT
wenzelm@34485
   112
        else
wenzelm@34485
   113
          Token.Kind.OTHER
immler@34551
   114
      val new_token = new Token(matcher.group, kind)
immler@34551
   115
      start += (new_token -> (match_start + matcher.start))
immler@34526
   116
      new_tokens ::= new_token
wenzelm@34318
   117
immler@34551
   118
      invalid_tokens = invalid_tokens dropWhile (stop(_) < stop(new_token))
immler@34526
   119
      invalid_tokens match {
wenzelm@34582
   120
        case t :: ts =>
wenzelm@34582
   121
          if (start(t) == start(new_token) &&
wenzelm@34582
   122
              start(t) > change.start + change.added.length) {
immler@34597
   123
          old_suffix = t :: ts
immler@34592
   124
          new_tokens = new_tokens.tail
immler@34526
   125
          invalid_tokens = Nil
immler@34526
   126
        }
immler@34526
   127
        case _ =>
wenzelm@34318
   128
      }
wenzelm@34318
   129
    }
immler@34526
   130
    val insert = new_tokens.reverse
immler@34544
   131
    val new_token_list = begin ::: insert ::: old_suffix
wenzelm@34582
   132
    token_changed(change.id, begin.lastOption, insert,
immler@34597
   133
      old_suffix.firstOption, new_token_list, start)
wenzelm@34318
   134
  }
wenzelm@34582
   135
wenzelm@34318
   136
  
wenzelm@34485
   137
  /** command view **/
wenzelm@34485
   138
wenzelm@34582
   139
  private def token_changed(
wenzelm@34582
   140
    new_id: String,
wenzelm@34582
   141
    before_change: Option[Token],
wenzelm@34582
   142
    inserted_tokens: List[Token],
wenzelm@34582
   143
    after_change: Option[Token],
immler@34597
   144
    new_tokens: List[Token],
wenzelm@34582
   145
    new_token_start: Map[Token, Int]): (ProofDocument, StructureChange) =
wenzelm@34485
   146
  {
immler@34595
   147
    val new_tokenset = (LinearSet() ++ new_tokens).asInstanceOf[LinearSet[Token]]
immler@34593
   148
    val cmd_before_change = before_change match {
immler@34593
   149
      case None => None
immler@34593
   150
      case Some(bc) =>
immler@34593
   151
        val cmd_with_bc = commands.find(_.contains(bc)).get
immler@34593
   152
        if (cmd_with_bc.tokens.last == bc) {
immler@34593
   153
          if (new_tokenset.next(bc).map(_.is_start).getOrElse(true))
immler@34593
   154
            Some(cmd_with_bc)
immler@34593
   155
          else commands.prev(cmd_with_bc)
immler@34593
   156
        }
immler@34593
   157
        else commands.prev(cmd_with_bc)
immler@34593
   158
    }
immler@34544
   159
immler@34593
   160
    val cmd_after_change = after_change match {
immler@34593
   161
      case None => None
immler@34593
   162
      case Some(ac) =>
immler@34593
   163
        val cmd_with_ac = commands.find(_.contains(ac)).get
immler@34593
   164
        if (ac.is_start)
immler@34593
   165
          Some(cmd_with_ac)
immler@34593
   166
        else
immler@34593
   167
          commands.next(cmd_with_ac)
immler@34593
   168
    }
wenzelm@34485
   169
immler@34593
   170
    val removed_commands = commands.dropWhile(Some(_) != cmd_before_change).drop(1).
immler@34554
   171
      takeWhile(Some(_) != cmd_after_change)
immler@34554
   172
immler@34554
   173
    // calculate inserted commands
immler@34526
   174
    def tokens_to_commands(tokens: List[Token]): List[Command]= {
immler@34526
   175
      tokens match {
immler@34526
   176
        case Nil => Nil
wenzelm@34582
   177
        case t :: ts =>
wenzelm@34582
   178
          val (cmd, rest) =
wenzelm@34582
   179
            ts.span(t => t.kind != Token.Kind.COMMAND_START && t.kind != Token.Kind.COMMENT)
wenzelm@34582
   180
          new Command(t :: cmd, new_token_start) :: tokens_to_commands(rest)
wenzelm@34485
   181
      }
wenzelm@34485
   182
    }
wenzelm@34485
   183
immler@34593
   184
    val split_begin =
immler@34593
   185
      if (before_change.isDefined) {
immler@34593
   186
        val changed =
immler@34593
   187
          if (cmd_before_change.isDefined)
immler@34595
   188
            new_tokens.dropWhile(_ != cmd_before_change.get.tokens.last).drop(1)
immler@34593
   189
          else new_tokenset
immler@34593
   190
        if (changed.exists(_ == before_change.get))
immler@34597
   191
          changed.takeWhile(_ != before_change.get).toList :::
immler@34597
   192
            List(before_change.get)
immler@34593
   193
        else Nil
immler@34593
   194
      } else Nil
immler@34554
   195
immler@34593
   196
    val split_end =
immler@34593
   197
      if (after_change.isDefined && cmd_after_change.isDefined) {
immler@34595
   198
        val unchanged = new_tokens.dropWhile(_ != after_change.get)
immler@34593
   199
        if (unchanged.exists(_ == cmd_after_change.get.tokens.first))
immler@34593
   200
          unchanged.takeWhile(_ != cmd_after_change.get.tokens.first).toList
immler@34593
   201
        else Nil
immler@34593
   202
      } else Nil
immler@34593
   203
immler@34597
   204
    val rescan_begin =
immler@34597
   205
      split_begin :::
immler@34597
   206
        before_change.map(bc => new_tokens.dropWhile(_ != bc).drop(1)).getOrElse(new_tokens)
wenzelm@34582
   207
    val rescanning_tokens =
immler@34597
   208
      after_change.map(ac => rescan_begin.takeWhile(_ != ac)).getOrElse(rescan_begin) :::
immler@34597
   209
        split_end
immler@34593
   210
    val inserted_commands = tokens_to_commands(rescanning_tokens.toList)
immler@34554
   211
immler@34554
   212
    val change = new StructureChange(cmd_before_change, inserted_commands, removed_commands.toList)
immler@34550
   213
    // build new document
immler@34554
   214
    val new_commandset = commands.delete_between(cmd_before_change, cmd_after_change).
immler@34554
   215
        insert_after(cmd_before_change, inserted_commands)
immler@34554
   216
immler@34544
   217
    val doc =
wenzelm@34582
   218
      new ProofDocument(new_id, new_tokenset, new_token_start,
immler@34653
   219
        new_commandset, states -- removed_commands, is_command_keyword)
immler@34538
   220
    return (doc, change)
wenzelm@34485
   221
  }
immler@34596
   222
immler@34596
   223
  val commands_offsets = {
immler@34596
   224
    var last_stop = 0
immler@34596
   225
    (for (c <- commands) yield {
immler@34596
   226
      val r = c -> (last_stop,c.stop(this))
immler@34596
   227
      last_stop = c.stop(this)
immler@34596
   228
      r
immler@34596
   229
    }).toArray
immler@34596
   230
  }
immler@34596
   231
immler@34596
   232
  // use a binary search to find commands for a given offset
immler@34596
   233
  def find_command_at(pos: Int): Command = find_command_at(pos, 0, commands_offsets.length)
immler@34596
   234
  private def find_command_at(pos: Int, array_start: Int, array_stop: Int): Command = {
immler@34596
   235
    val middle_index = (array_start + array_stop) / 2
immler@34596
   236
    if (middle_index >= commands_offsets.length) return null
immler@34596
   237
    val (middle, (start, stop)) = commands_offsets(middle_index)
immler@34596
   238
    // does middle contain pos?
immler@34596
   239
    if (start <= pos && stop > pos)
immler@34596
   240
      middle
immler@34596
   241
    else if (start > pos)
immler@34596
   242
      find_command_at(pos, array_start, middle_index)
immler@34596
   243
    else if (stop <= pos)
immler@34596
   244
      find_command_at(pos, middle_index + 1, array_stop)
immler@34596
   245
    else error("can't be")
immler@34596
   246
  }
immler@34596
   247
wenzelm@34318
   248
}