src/Pure/PIDE/document.scala
changeset 38424 940a404e45e2
parent 38418 9a7af64d71bb
child 38425 e467db701d78
equal deleted inserted replaced
38423:a9cff3f2e479 38424:940a404e45e2
    28 
    28 
    29   val NO_ID: ID = 0
    29   val NO_ID: ID = 0
    30 
    30 
    31 
    31 
    32 
    32 
    33   /** named document nodes **/
    33   /** document structure **/
       
    34 
       
    35   /* named nodes -- development graph */
       
    36 
       
    37   type Node_Text_Edit = (String, List[Text_Edit])  // FIXME None: remove
       
    38 
       
    39   type Edit[C] =
       
    40    (String,  // node name
       
    41     Option[List[(Option[C], Option[C])]])  // None: remove, Some: insert/remove commands
    34 
    42 
    35   object Node
    43   object Node
    36   {
    44   {
    37     val empty: Node = new Node(Linear_Set())
    45     val empty: Node = new Node(Linear_Set())
    38 
    46 
       
    47     // FIXME not scalable
    39     def command_starts(commands: Iterator[Command], offset: Int = 0): Iterator[(Command, Int)] =
    48     def command_starts(commands: Iterator[Command], offset: Int = 0): Iterator[(Command, Int)] =
    40     {
    49     {
    41       var i = offset
    50       var i = offset
    42       for (command <- commands) yield {
    51       for (command <- commands) yield {
    43         val start = i
    52         val start = i
    47     }
    56     }
    48   }
    57   }
    49 
    58 
    50   class Node(val commands: Linear_Set[Command])
    59   class Node(val commands: Linear_Set[Command])
    51   {
    60   {
    52     /* command ranges */
       
    53 
       
    54     def command_starts: Iterator[(Command, Int)] =
    61     def command_starts: Iterator[(Command, Int)] =
    55       Node.command_starts(commands.iterator)
    62       Node.command_starts(commands.iterator)
    56 
    63 
    57     def command_start(cmd: Command): Option[Int] =
    64     def command_start(cmd: Command): Option[Int] =
    58       command_starts.find(_._1 == cmd).map(_._2)
    65       command_starts.find(_._1 == cmd).map(_._2)
    76         case None => None
    83         case None => None
    77       }
    84       }
    78   }
    85   }
    79 
    86 
    80 
    87 
    81   /* document versions */
    88 
       
    89   /** versioning **/
       
    90 
       
    91   /* particular document versions */
    82 
    92 
    83   object Version
    93   object Version
    84   {
    94   {
    85     val init: Version = new Version(NO_ID, Map().withDefaultValue(Node.empty))
    95     val init: Version = new Version(NO_ID, Map().withDefaultValue(Node.empty))
    86   }
    96   }
    87 
    97 
    88   class Version(val id: Version_ID, val nodes: Map[String, Node])
    98   class Version(val id: Version_ID, val nodes: Map[String, Node])
    89 
    99 
    90 
   100 
    91 
   101   /* changes of plain text, eventually resulting in document edits */
    92   /** changes of plain text, eventually resulting in document edits **/
   102 
    93 
   103   object Change
    94   type Node_Text_Edit = (String, List[Text_Edit])  // FIXME None: remove
   104   {
    95 
   105     val init = new Change(Future.value(Version.init), Nil, Future.value(Nil, Version.init))
    96   type Edit[C] =
   106   }
    97    (String,  // node name
   107 
    98     Option[List[(Option[C], Option[C])]])  // None: remove, Some: insert/remove commands
   108   class Change(
       
   109     val previous: Future[Version],
       
   110     val edits: List[Node_Text_Edit],
       
   111     val result: Future[(List[Edit[Command]], Version)])
       
   112   {
       
   113     val current: Future[Version] = result.map(_._2)
       
   114     def is_finished: Boolean = previous.is_finished && current.is_finished
       
   115   }
       
   116 
       
   117 
       
   118   /* history navigation and persistent snapshots */
    99 
   119 
   100   abstract class Snapshot
   120   abstract class Snapshot
   101   {
   121   {
   102     val version: Version
   122     val version: Version
   103     val node: Node
   123     val node: Node
   106     def revert(offset: Int): Int
   126     def revert(offset: Int): Int
   107     def lookup_command(id: Command_ID): Option[Command]
   127     def lookup_command(id: Command_ID): Option[Command]
   108     def state(command: Command): Command.State
   128     def state(command: Command): Command.State
   109   }
   129   }
   110 
   130 
   111   object Change
   131   object History
   112   {
   132   {
   113     val init = new Change(Future.value(Version.init), Nil, Future.value(Nil, Version.init))
   133     val init = new History(List(Change.init))
   114   }
   134   }
   115 
   135 
   116   class Change(
   136   // FIXME pruning, purging of state
   117     val previous: Future[Version],
   137   class History(undo_list: List[Change])
   118     val edits: List[Node_Text_Edit],
   138   {
   119     val result: Future[(List[Edit[Command]], Version)])
   139     require(!undo_list.isEmpty)
   120   {
   140 
   121     val current: Future[Version] = result.map(_._2)
   141     def tip: Change = undo_list.head
   122     def is_finished: Boolean = previous.is_finished && current.is_finished
   142     def +(ch: Change): History = new History(ch :: undo_list)
       
   143 
       
   144     def snapshot(name: String, pending_edits: List[Text_Edit], state_snapshot: State): Snapshot =
       
   145     {
       
   146       val found_stable = undo_list.find(change =>
       
   147         change.is_finished && state_snapshot.is_assigned(change.current.join))
       
   148       require(found_stable.isDefined)
       
   149       val stable = found_stable.get
       
   150       val latest = undo_list.head
       
   151 
       
   152       val edits =
       
   153         (pending_edits /: undo_list.takeWhile(_ != stable))((edits, change) =>
       
   154             (for ((a, eds) <- change.edits if a == name) yield eds).flatten ::: edits)
       
   155       lazy val reverse_edits = edits.reverse
       
   156 
       
   157       new Snapshot
       
   158       {
       
   159         val version = stable.current.join
       
   160         val node = version.nodes(name)
       
   161         val is_outdated = !(pending_edits.isEmpty && latest == stable)
       
   162         def convert(offset: Int): Int = (offset /: edits)((i, edit) => edit.convert(i))
       
   163         def revert(offset: Int): Int = (offset /: reverse_edits)((i, edit) => edit.revert(i))
       
   164         def lookup_command(id: Command_ID): Option[Command] = state_snapshot.lookup_command(id)
       
   165         def state(command: Command): Command.State =
       
   166           try { state_snapshot.command_state(version, command) }
       
   167           catch { case _: State.Fail => command.empty_state }
       
   168       }
       
   169     }
   123   }
   170   }
   124 
   171 
   125 
   172 
   126 
   173 
   127   /** global state -- document structure and execution process **/
   174   /** global state -- document structure and execution process **/