From 4df0eeaa3dba7ec1fa651657435a4754d5cd4108 Mon Sep 17 00:00:00 2001 From: asynts Date: Thu, 3 Dec 2020 22:41:26 +0100 Subject: [PATCH] LibWeb: Join start and end after deleting selection. --- Libraries/LibWeb/Page/EditEventHandler.cpp | 65 +++++++++++++--------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/Libraries/LibWeb/Page/EditEventHandler.cpp b/Libraries/LibWeb/Page/EditEventHandler.cpp index 8a33ec6df25..5e8267faa2d 100644 --- a/Libraries/LibWeb/Page/EditEventHandler.cpp +++ b/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -38,46 +38,57 @@ namespace Web { +// This method is quite convoluted but this is necessary to make editing feel intuitive. void EditEventHandler::handle_delete(DOM::Range range) { auto* start = downcast(range.start().node()); auto* end = downcast(range.end().node()); - // Remove all the nodes that are fully enclosed in the range. - HashTable queued_for_deletion; - for (auto* node = start->next_in_pre_order(); node; node = node->next_in_pre_order()) { - if (node == end) - break; - - queued_for_deletion.set(node); - } - for (auto* parent = start->parent(); parent; parent = parent->parent()) - queued_for_deletion.remove(parent); - for (auto* parent = end->parent(); parent; parent = parent->parent()) - queued_for_deletion.remove(parent); - for (auto* node : queued_for_deletion) - node->parent()->remove_child(*node); - - if (start == end || start->next_sibling() == end) { - // If the start and end text nodes are now immediate siblings, merge the remainders into one. - + if (start == end) { StringBuilder builder; builder.append(start->data().substring_view(0, range.start().offset())); builder.append(end->data().substring_view(range.end().offset())); start->set_data(builder.to_string()); - start->invalidate_style(); - - if (start != end) - start->parent()->remove_child(*end); } else { - // Otherwise, remove parts from both nodes. + // Remove all the nodes that are fully enclosed in the range. + HashTable queued_for_deletion; + for (auto* node = start->next_in_pre_order(); node; node = node->next_in_pre_order()) { + if (node == end) + break; - start->set_data(start->data().substring_view(0, range.start().offset())); - start->invalidate_style(); + queued_for_deletion.set(node); + } + for (auto* parent = start->parent(); parent; parent = parent->parent()) + queued_for_deletion.remove(parent); + for (auto* parent = end->parent(); parent; parent = parent->parent()) + queued_for_deletion.remove(parent); + for (auto* node : queued_for_deletion) + node->parent()->remove_child(*node); - end->set_data(end->data().substring_view(range.end().offset())); - end->invalidate_style(); + // Join the parent nodes of start and end. + DOM::Node *insert_after = start, *remove_from = end, *parent_of_end = end->parent(); + while (remove_from) { + auto* next_sibling = remove_from->next_sibling(); + + remove_from->parent()->remove_child(*remove_from); + insert_after->parent()->insert_before(*remove_from, *insert_after); + + insert_after = remove_from; + remove_from = next_sibling; + } + if (!parent_of_end->has_children()) { + if (parent_of_end->parent()) + parent_of_end->parent()->remove_child(*parent_of_end); + } + + // Join the start and end nodes. + StringBuilder builder; + builder.append(start->data().substring_view(0, range.start().offset())); + builder.append(end->data().substring_view(range.end().offset())); + + start->set_data(builder.to_string()); + start->parent()->remove_child(*end); } // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still