48 def text_edits(session: Session, old_doc: Document, new_id: Isar_Document.Document_ID, |
47 def text_edits(session: Session, old_doc: Document, new_id: Isar_Document.Document_ID, |
49 edits: List[Text_Edit]): (List[Edit], Document) = |
48 edits: List[Text_Edit]): (List[Edit], Document) = |
50 { |
49 { |
51 require(old_doc.assignment.is_finished) |
50 require(old_doc.assignment.is_finished) |
52 |
51 |
53 phase0 = edits |
|
54 |
|
55 |
52 |
56 /* unparsed dummy commands */ |
53 /* unparsed dummy commands */ |
57 |
54 |
58 def unparsed(source: String) = |
55 def unparsed(source: String) = |
59 new Command(null, List(Outer_Lex.Token(Outer_Lex.Token_Kind.UNPARSED, source))) |
56 new Command(null, List(Outer_Lex.Token(Outer_Lex.Token_Kind.UNPARSED, source))) |
60 |
57 |
61 def is_unparsed(command: Command) = command.id == null |
58 def is_unparsed(command: Command) = command.id == null |
62 |
59 |
63 assert(!old_doc.commands.exists(is_unparsed)) |
60 assert(!old_doc.commands.exists(is_unparsed)) // FIXME remove |
64 |
61 |
65 |
62 |
66 /* phase 1: edit individual command source */ |
63 /* phase 1: edit individual command source */ |
67 |
64 |
68 var commands = old_doc.commands |
65 def edit_text(eds: List[Text_Edit], commands: Linear_Set[Command]): Linear_Set[Command] = |
69 |
|
70 def edit_text(eds: List[Text_Edit]): Unit = |
|
71 { |
66 { |
72 eds match { |
67 eds match { |
73 case e :: es => |
68 case e :: es => |
74 command_starts(commands).find { // FIXME relative search! |
69 command_starts(commands).find { // FIXME relative search! |
75 case (cmd, cmd_start) => e.can_edit(cmd.length, cmd_start) |
70 case (cmd, cmd_start) => e.can_edit(cmd.length, cmd_start) |
76 } match { // FIXME try to append after command |
71 } match { |
|
72 // FIXME try to append after command |
77 case Some((cmd, cmd_start)) => |
73 case Some((cmd, cmd_start)) => |
78 val (rest, source) = e.edit(cmd.source, cmd_start) |
74 val (rest, source) = e.edit(cmd.source, cmd_start) |
79 // FIXME Linear_Set.edit (?) |
75 val new_commands = commands.insert_after(Some(cmd), unparsed(source)) - cmd |
80 commands = commands.insert_after(Some(cmd), unparsed(source)) |
76 edit_text(rest.toList ::: es, new_commands) |
81 commands -= cmd |
|
82 edit_text(rest.toList ::: es) |
|
83 |
77 |
84 case None => |
78 case None => |
85 require(e.isInstanceOf[Text_Edit.Insert] && e.start == 0) |
79 require(e.isInstanceOf[Text_Edit.Insert] && e.start == 0) |
86 commands = commands.insert_after(None, unparsed(e.text)) |
80 edit_text(es, commands.insert_after(None, unparsed(e.text))) |
87 edit_text(es) |
|
88 } |
81 } |
89 case Nil => |
82 case Nil => commands |
90 } |
83 } |
91 } |
84 } |
92 edit_text(edits) |
|
93 |
|
94 phase1 = commands |
|
95 |
85 |
96 |
86 |
97 /* phase 2: command range edits */ |
87 /* phase 2: command range edits */ |
98 |
88 |
99 def edit_commands(): Unit = |
89 def edit_commands(commands: Linear_Set[Command]): Linear_Set[Command] = |
100 { |
90 { |
101 // FIXME relative search! |
91 // FIXME relative search! |
102 commands.elements.find(is_unparsed) match { |
92 commands.elements.find(is_unparsed) match { |
103 case Some(first_unparsed) => |
93 case Some(first_unparsed) => |
104 val prefix = commands.prev(first_unparsed) |
94 val prefix = commands.prev(first_unparsed) |
105 val body = commands.elements(first_unparsed).takeWhile(is_unparsed).toList |
95 val body = commands.elements(first_unparsed).takeWhile(is_unparsed).toList |
106 val suffix = commands.next(body.last) |
96 val suffix = commands.next(body.last) |
107 |
97 |
108 val source = |
98 val sources = (prefix.toList ::: body ::: suffix.toList).flatMap(_.span.map(_.source)) |
109 (prefix.toList ::: body ::: suffix.toList).flatMap(_.span.map(_.source)).mkString |
99 val span = Thy_Syntax.parse_spans(session.current_syntax.scan(sources.mkString)) |
110 assert(source != "") |
|
111 raw_source = source |
|
112 |
|
113 val spans0 = Thy_Syntax.parse_spans(session.current_syntax.scan(source)) |
|
114 |
100 |
115 val (before_edit, spans1) = |
101 val (before_edit, spans1) = |
116 if (!spans0.isEmpty && Some(spans0.first) == prefix.map(_.span)) |
102 if (!span.isEmpty && Some(span.first) == prefix.map(_.span)) |
117 (prefix, spans0.tail) |
103 (prefix, span.tail) |
118 else (if (prefix.isDefined) commands.prev(prefix.get) else None, spans0) |
104 else (if (prefix.isDefined) commands.prev(prefix.get) else None, span) |
119 |
105 |
120 val (after_edit, spans2) = |
106 val (after_edit, spans2) = |
121 if (!spans1.isEmpty && Some(spans1.last) == suffix.map(_.span)) |
107 if (!spans1.isEmpty && Some(spans1.last) == suffix.map(_.span)) |
122 (suffix, spans1.take(spans1.length - 1)) |
108 (suffix, spans1.take(spans1.length - 1)) |
123 else (if (suffix.isDefined) commands.next(suffix.get) else None, spans1) |
109 else (if (suffix.isDefined) commands.next(suffix.get) else None, spans1) |
124 |
110 |
125 val new_commands = spans2.map(span => new Command(session.create_id(), span)) |
111 val inserted = spans2.map(span => new Command(session.create_id(), span)) |
|
112 val new_commands = |
|
113 commands.delete_between(before_edit, after_edit).append_after(before_edit, inserted) |
|
114 edit_commands(new_commands) |
126 |
115 |
127 commands = commands.delete_between(before_edit, after_edit) |
116 case None => commands |
128 commands = commands.append_after(before_edit, new_commands) |
|
129 |
|
130 edit_commands() |
|
131 |
|
132 case None => |
|
133 } |
117 } |
134 } |
118 } |
135 edit_commands() |
|
136 |
|
137 phase2 = commands |
|
138 |
119 |
139 |
120 |
140 /* phase 3: resulting document edits */ |
121 /* phase 3: resulting document edits */ |
141 |
122 |
142 val removed_commands = old_doc.commands.elements.filter(!commands.contains(_)).toList |
123 val commands0 = old_doc.commands |
143 val inserted_commands = commands.elements.filter(!old_doc.commands.contains(_)).toList |
124 val commands1 = edit_text(edits, commands0) |
|
125 val commands2 = edit_commands(commands1) |
144 |
126 |
145 // FIXME proper order |
127 val removed_commands = commands0.elements.filter(!commands2.contains(_)).toList |
|
128 val inserted_commands = commands2.elements.filter(!commands0.contains(_)).toList |
|
129 |
|
130 // FIXME tune |
146 val doc_edits = |
131 val doc_edits = |
147 removed_commands.reverse.map(cmd => (commands.prev(cmd), None)) ::: |
132 removed_commands.reverse.map(cmd => (commands0.prev(cmd), None)) ::: |
148 inserted_commands.map(cmd => (commands.prev(cmd), Some(cmd))) |
133 inserted_commands.map(cmd => (commands2.prev(cmd), Some(cmd))) |
149 |
134 |
150 val former_states = old_doc.assignment.join -- removed_commands |
135 val former_states = old_doc.assignment.join -- removed_commands |
151 |
136 |
|
137 phase0 = edits |
|
138 phase1 = commands1 |
|
139 phase2 = commands2 |
152 phase3 = doc_edits |
140 phase3 = doc_edits |
153 (doc_edits, new Document(new_id, commands, former_states)) |
141 |
|
142 (doc_edits, new Document(new_id, commands2, former_states)) |
154 } |
143 } |
155 } |
144 } |
156 |
145 |
157 |
146 |
158 class Document( |
147 class Document( |