delete single characters
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SimpleEditingTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SimpleEditingTest.java
index cd52547..08569da 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SimpleEditingTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SimpleEditingTest.java
@@ -13,12 +13,15 @@
import static org.eclipse.vex.core.internal.widget.VexWidgetTest.createDocumentWithDTD;
import static org.eclipse.vex.core.tests.TestResources.TEST_DTD;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.vex.core.internal.css.StyleSheet;
import org.eclipse.vex.core.internal.dom.Element;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -26,6 +29,9 @@
*/
public class L2SimpleEditingTest {
+ private static final QualifiedName TITLE = new QualifiedName(null, "title");
+ private static final QualifiedName PARA = new QualifiedName(null, "para");
+
private VexWidgetImpl widget;
private Element rootElement;
@@ -44,13 +50,13 @@
@Test
public void shouldMoveCaretIntoInsertedElement() throws Exception {
- final Element titleElement = widget.insertElement(new QualifiedName(null, "title"));
+ final Element titleElement = widget.insertElement(TITLE);
assertEquals(titleElement.getEndOffset(), widget.getCaretOffset());
}
@Test
public void shouldProvideInsertionElementAsCurrentElement() throws Exception {
- final Element titleElement = widget.insertElement(new QualifiedName(null, "title"));
+ final Element titleElement = widget.insertElement(TITLE);
widget.moveBy(-1);
assertEquals(titleElement.getStartOffset(), widget.getCaretOffset());
assertSame(rootElement, widget.getCurrentElement());
@@ -58,7 +64,7 @@
@Test
public void givenAnElementWithText_whenHittingBackspace_shouldDeleteLastCharacter() throws Exception {
- final Element titleElement = widget.insertElement(new QualifiedName(null, "title"));
+ final Element titleElement = widget.insertElement(TITLE);
widget.insertText("Hello");
widget.deletePreviousChar();
assertEquals("Hell", titleElement.getText());
@@ -67,11 +73,36 @@
@Test
public void givenAnElementWithText_whenHittingPos1AndDelete_shouldDeleteFirstCharacter() throws Exception {
- final Element titleElement = widget.insertElement(new QualifiedName(null, "title"));
+ final Element titleElement = widget.insertElement(TITLE);
widget.insertText("Hello");
widget.moveBy(-5);
widget.deleteNextChar();
assertEquals("ello", titleElement.getText());
assertEquals(titleElement.getStartOffset() + 1, widget.getCaretOffset());
}
+
+ @Test
+ @Ignore("get moveTo working first")
+ public void givenAnEmptyElement_whenCaretBetweenStartAndEndTagAndHittingBackspace_shouldDeleteEmptyElement() throws Exception {
+ widget.insertElement(TITLE);
+ widget.moveBy(1);
+ final Element paraElement = widget.insertElement(PARA);
+ widget.deletePreviousChar();
+ assertEquals(1, rootElement.getChildCount());
+ assertNull(paraElement.getParent());
+ assertFalse(paraElement.isAssociated());
+ }
+
+ @Test
+ @Ignore("get moveTo working first")
+ public void givenAnEmptyElement_whenCaretBeforeStartOffsetAndHittingDelete_shouldDeleteEmptyElement() throws Exception {
+ widget.insertElement(TITLE);
+ widget.moveBy(1);
+ final Element paraElement = widget.insertElement(PARA);
+ widget.moveBy(-1);
+ widget.deleteNextChar();
+ assertEquals(1, rootElement.getChildCount());
+ assertNull(paraElement.getParent());
+ assertFalse(paraElement.isAssociated());
+ }
}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
index fec52ff..97d7abd 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Document.java
@@ -328,13 +328,20 @@
}
public void delete(final Range range) throws DocumentValidationException {
- final Parent surroundingParent = getParentAt(range.getStartOffset() - 1);
- final Parent parentAtEndOffset = getParentAt(range.getEndOffset() + 1);
+ final Parent surroundingParent = getParentAt(range.getStartOffset());
+ final Parent parentAtEndOffset = getParentAt(range.getEndOffset());
if (surroundingParent != parentAtEndOffset) {
throw new IllegalArgumentException("Deletion in " + range + " is unbalanced");
}
- final boolean deletionIsValid = surroundingParent.accept(new BaseNodeVisitorWithResult<Boolean>(true) {
+ final Parent parentForDeletion;
+ if (range.equals(surroundingParent.getRange())) {
+ parentForDeletion = surroundingParent.getParent();
+ } else {
+ parentForDeletion = surroundingParent;
+ }
+
+ final boolean deletionIsValid = parentForDeletion.accept(new BaseNodeVisitorWithResult<Boolean>(true) {
@Override
public Boolean visit(final Element element) {
final Validator validator = getValidator();
@@ -350,19 +357,20 @@
throw new DocumentValidationException("Unable to delete " + range);
}
- fireBeforeContentDeleted(new DocumentEvent(this, surroundingParent, range.getStartOffset(), range.length(), null));
+ fireBeforeContentDeleted(new DocumentEvent(this, parentForDeletion, range.getStartOffset(), range.length(), null));
- final Iterator<Node> iter = surroundingParent.getChildIterator();
+ final Iterator<Node> iter = parentForDeletion.getChildIterator();
while (iter.hasNext()) {
final Node child = iter.next();
if (child.isInRange(range)) {
- surroundingParent.removeChild(child);
+ parentForDeletion.removeChild(child);
+ child.dissociate();
}
}
getContent().remove(range);
- fireContentDeleted(new DocumentEvent(this, surroundingParent, range.getStartOffset(), range.length(), null));
+ fireContentDeleted(new DocumentEvent(this, parentForDeletion, range.getStartOffset(), range.length(), null));
}
/*
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/AbstractBlockBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/AbstractBlockBox.java
index 357acf9..da68ad6 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/AbstractBlockBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/AbstractBlockBox.java
@@ -376,7 +376,7 @@
@Override
public boolean hasContent() {
- return true;
+ return getNode().isAssociated();
}
public void invalidate(final boolean direct) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/BlockElementBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/BlockElementBox.java
index b92f70c..57a3a0d 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/BlockElementBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/BlockElementBox.java
@@ -76,11 +76,6 @@
}
@Override
- public boolean hasContent() {
- return true;
- }
-
- @Override
public void paint(final LayoutContext context, final int x, final int y) {
super.paint(context, x, y);
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CompositeInlineBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CompositeInlineBox.java
index 0862e3c..63cb2c1 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CompositeInlineBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/CompositeInlineBox.java
@@ -33,6 +33,10 @@
*/
@Override
public boolean hasContent() {
+ if (!getNode().isAssociated()) {
+ return false;
+ }
+
final Box[] children = getChildren();
for (final Box element : children) {
if (element.hasContent()) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/DocumentTextBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/DocumentTextBox.java
index 873a944..5d69168 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/DocumentTextBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/DocumentTextBox.java
@@ -104,7 +104,7 @@
*/
@Override
public boolean hasContent() {
- return true;
+ return getNode().isAssociated();
}
/**
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/LineBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/LineBox.java
index d0befae..c361922 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/LineBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/LineBox.java
@@ -98,7 +98,7 @@
*/
@Override
public boolean hasContent() {
- return firstContentChild != null;
+ return firstContentChild != null && firstContentChild.hasContent();
}
/**
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java
index 7f13e7c..dad0308 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/layout/ParagraphBox.java
@@ -273,7 +273,7 @@
@Override
public boolean hasContent() {
- return firstContentLine != null;
+ return firstContentLine != null && firstContentLine.hasContent();
}
public IntRange layout(final LayoutContext context, final int top, final int bottom) {
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
index 2b3d768..82c7be3 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/VexWidgetImpl.java
@@ -321,18 +321,15 @@
} else if (element.isEmpty()) {
// deleting the right sentinel of an empty element
// so just delete the whole element an move on
- this.moveTo(offset - 1, false);
- this.moveTo(offset + 1, true);
+ this.moveTo(offset - 1, true);
deleteSelection();
} else if (doc.getElementForInsertionAt(offset + 1).isEmpty()) {
// deleting the left sentinel of an empty element
// so just delete the whole element an move on
- this.moveTo(offset + 2, true);
- deleteSelection();
- } else if (!doc.isElementAt(offset)) {
- this.moveTo(offset, false);
this.moveTo(offset + 1, true);
deleteSelection();
+ } else if (!doc.isElementAt(offset)) {
+ deleteOffset();
}
}
}
@@ -354,29 +351,37 @@
} else if (element.isEmpty()) {
// deleting the left sentinel of an empty element
// so just delete the whole element an move on
- this.moveTo(offset - 1, false);
- this.moveTo(offset + 1, true);
+ this.moveTo(offset - 1, true);
deleteSelection();
} else if (doc.getElementForInsertionAt(offset - 1).isEmpty()) {
// deleting the right sentinel of an empty element
// so just delete the whole element an move on
+ this.moveTo(offset - 1, false);
this.moveTo(offset - 2, true);
deleteSelection();
} else {
offset--;
if (!doc.isElementAt(offset)) {
- this.moveTo(offset, false);
- this.moveTo(offset + 1, true);
- deleteSelection();
+ moveBy(-1);
+ deleteOffset();
}
}
}
}
+ private void deleteOffset() {
+ try {
+ applyEdit(new DeleteEdit(document, new Range(getCaretOffset(), getCaretOffset())), getCaretOffset());
+ this.moveTo(getSelectionStart());
+ } catch (final DocumentValidationException e) {
+ e.printStackTrace(); // This should never happen, because we constrain the selection
+ }
+ }
+
public void deleteSelection() {
try {
if (hasSelection()) {
- applyEdit(new DeleteEdit(document, new Range(getSelectionStart(), getSelectionEnd() - 1)), getSelectionStart());
+ applyEdit(new DeleteEdit(document, new Range(getSelectionStart(), getSelectionEnd())), getSelectionStart());
this.moveTo(getSelectionStart());
}
} catch (final DocumentValidationException e) {