test simple selection operations

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentTest.java
index 9db8e31..691d826 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/DocumentTest.java
@@ -52,15 +52,6 @@
 		assertDocumentConnectedToRootElement(rootElement, document);

 	}

 

-	private static void assertDocumentConnectedToRootElement(final Element rootElement, final Document document) {

-		assertNotNull(document.getContent());

-		assertTrue(document.isAssociated());

-		assertTrue(rootElement.isAssociated());

-		assertSame(document, rootElement.getParent());

-		assertTrue(rootElement.getStartOffset() >= document.getStartOffset());

-		assertTrue(rootElement.getEndOffset() <= document.getEndOffset());

-	}

-

 	@Test

 	public void createFragmentWithTextAndChild() throws Exception {

 		final Document document = new Document(new Element("root"));

@@ -85,6 +76,17 @@
 		assertNodesEqual(document.getNodes(range), fragment.getNodes());

 	}

 

+	@Test

+	public void givenElementWithText_whenRangeBeginsFromStartOffset_shouldProvideParentAsCommenElement() throws Exception {

+		final Document document = new Document(new Element("root"));

+		final Element childElement = document.insertElement(1, new QualifiedName(null, "child"));

+		document.insertText(childElement.getEndOffset(), "Hello World");

+

+		final Element commonElement = document.findCommonElement(childElement.getStartOffset(), childElement.getEndOffset() - 5);

+

+		assertSame(document.getRootElement(), commonElement);

+	}

+

 	private static void assertNodesEqual(final List<? extends Node> expected, final List<? extends Node> actual) {

 		assertEquals(expected.size(), actual.size());

 		for (int i = 0; i < expected.size(); i++) {

@@ -99,4 +101,14 @@
 			assertNodesEqual(((Parent) expected).getChildNodes(), ((Parent) actual).getChildNodes());

 		}

 	}

+

+	private static void assertDocumentConnectedToRootElement(final Element rootElement, final Document document) {

+		assertNotNull(document.getContent());

+		assertTrue(document.isAssociated());

+		assertTrue(rootElement.isAssociated());

+		assertSame(document, rootElement.getParent());

+		assertTrue(rootElement.getStartOffset() >= document.getStartOffset());

+		assertTrue(rootElement.getEndOffset() <= document.getEndOffset());

+	}

+

 }

diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SelectionTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SelectionTest.java
new file mode 100644
index 0000000..269981a
--- /dev/null
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/L2SelectionTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************

+ * Copyright (c) 2012 Florian Thienel and others.

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * which accompanies this distribution, and is available at

+ * http://www.eclipse.org/legal/epl-v10.html

+ * 

+ * Contributors:

+ * 		Florian Thienel - initial API and implementation

+ *******************************************************************************/

+package org.eclipse.vex.core.internal.widget;

+

+import static org.eclipse.vex.core.internal.widget.VexWidgetTest.TITLE;

+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.assertTrue;

+

+import org.eclipse.vex.core.internal.css.StyleSheet;

+import org.eclipse.vex.core.internal.dom.Element;

+import org.junit.Before;

+import org.junit.Test;

+

+/**

+ * @author Florian Thienel

+ */

+public class L2SelectionTest {

+

+	private VexWidgetImpl widget;

+	private Element rootElement;

+

+	@Before

+	public void setUp() throws Exception {

+		widget = new VexWidgetImpl(new MockHostComponent());

+		widget.setDocument(createDocumentWithDTD(TEST_DTD, "section"), StyleSheet.NULL);

+		rootElement = widget.getDocument().getRootElement();

+	}

+

+	@Test

+	public void givenCaretInElement_whenSelectionIncludesStartOffset_shouldExpandSelectionToEndOffset() throws Exception {

+		final Element titleElement = widget.insertElement(TITLE);

+		widget.moveBy(-1, true);

+		assertTrue(widget.hasSelection());

+		assertEquals(titleElement.getStartOffset(), widget.getSelectionStart());

+		assertEquals(titleElement.getEndOffset(), widget.getSelectionEnd());

+	}

+

+	@Test

+	public void givenCaretInElementWith_whenSelectionIncludesStartOffset_shouldExpandSelectionToEndOffset() throws Exception {

+		final Element titleElement = widget.insertElement(TITLE);

+		widget.insertText("Hello World");

+		widget.moveBy(-5, false);

+		widget.moveTo(titleElement.getStartOffset(), true);

+		assertTrue(widget.hasSelection());

+		assertEquals(titleElement.getStartOffset(), widget.getSelectionStart());

+		assertEquals(titleElement.getEndOffset(), widget.getSelectionEnd());

+	}

+

+	@Test

+	public void givenCaretInElementAtEndOffset_whenMovedByOneBehindEndOffset_shouldExpandSelectionToStartOffset() throws Exception {

+		final Element titleElement = widget.insertElement(TITLE);

+		widget.insertText("Hello World");

+		widget.moveBy(1, true);

+		assertTrue(widget.hasSelection());

+		assertEquals(titleElement.getStartOffset(), widget.getSelectionStart());

+		assertEquals(titleElement.getEndOffset() + 1, widget.getSelectionEnd());

+	}

+}

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 08569da..6f0d5dc 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
@@ -10,6 +10,8 @@
  *******************************************************************************/

 package org.eclipse.vex.core.internal.widget;

 

+import static org.eclipse.vex.core.internal.widget.VexWidgetTest.PARA;

+import static org.eclipse.vex.core.internal.widget.VexWidgetTest.TITLE;

 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;

@@ -17,11 +19,9 @@
 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;

 

 /**

@@ -29,9 +29,6 @@
  */

 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;

 

@@ -82,7 +79,6 @@
 	}

 

 	@Test

-	@Ignore("get moveTo working first")

 	public void givenAnEmptyElement_whenCaretBetweenStartAndEndTagAndHittingBackspace_shouldDeleteEmptyElement() throws Exception {

 		widget.insertElement(TITLE);

 		widget.moveBy(1);

@@ -94,7 +90,6 @@
 	}

 

 	@Test

-	@Ignore("get moveTo working first")

 	public void givenAnEmptyElement_whenCaretBeforeStartOffsetAndHittingDelete_shouldDeleteEmptyElement() throws Exception {

 		widget.insertElement(TITLE);

 		widget.moveBy(1);

diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/VexWidgetTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/VexWidgetTest.java
index 3a82c6c..c63bf0c 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/VexWidgetTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/widget/VexWidgetTest.java
@@ -31,6 +31,9 @@
 
 public class VexWidgetTest {
 
+	static final QualifiedName TITLE = new QualifiedName(null, "title");
+	static final QualifiedName PARA = new QualifiedName(null, "para");
+
 	@Test
 	public void provideOnlyAllowedElementsFromDtd() throws Exception {
 		final VexWidgetImpl widget = new VexWidgetImpl(new MockHostComponent());
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
index 79dad79..dcfacd7 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/tests/VEXCoreTestSuite.java
@@ -49,6 +49,7 @@
 import org.eclipse.vex.core.internal.layout.TestBlocksInInlines;
 import org.eclipse.vex.core.internal.layout.TestDocumentTextBox;
 import org.eclipse.vex.core.internal.layout.TestStaticTextBox;
+import org.eclipse.vex.core.internal.widget.L2SelectionTest;
 import org.eclipse.vex.core.internal.widget.L2SimpleEditingTest;
 import org.eclipse.vex.core.internal.widget.VexWidgetTest;
 
@@ -95,5 +96,6 @@
 		addTestSuite(ListenerListTest.class);
 		addTest(new JUnit4TestAdapter(VexWidgetTest.class));
 		addTest(new JUnit4TestAdapter(L2SimpleEditingTest.class));
+		addTest(new JUnit4TestAdapter(L2SelectionTest.class));
 	}
 }
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 97d7abd..345b4bf 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
@@ -397,7 +397,7 @@
 			final List<Element> children = element.getChildElements();
 			for (int i = 0; i < children.size(); i++) {
 				final Element child = children.get(i);
-				if (child.containsOffset(offset1) && child.containsOffset(offset2)) {
+				if (isInsertionPointIn(child, offset1) && isInsertionPointIn(child, offset2)) {
 					element = child;
 					tryAgain = true;
 					break;
@@ -410,6 +410,10 @@
 		return element;
 	}
 
+	private static boolean isInsertionPointIn(final Node node, final int offset) {
+		return node.containsOffset(offset) && offset > node.getStartOffset();
+	}
+
 	private Node getNodeForInsertionAt(final int offset) {
 		final Node node = getChildNodeAt(offset);
 		if (offset == node.getStartOffset()) {
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 82c7be3..28b6169 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
@@ -846,8 +846,7 @@
 				selectionStart = Math.min(mark, caretOffset);
 				selectionEnd = Math.max(mark, caretOffset);
 
-				// move selectionStart and selectionEnd to make sure we don't
-				// select a partial element
+				// move selectionStart and selectionEnd to make sure we don't select a partial element
 				final Element commonElement = document.findCommonElement(selectionStart, selectionEnd);
 
 				Element element = document.getElementForInsertionAt(selectionStart);
@@ -858,8 +857,8 @@
 
 				element = document.getElementForInsertionAt(selectionEnd);
 				while (element != commonElement) {
-					selectionEnd = element.getEndOffset() + 1;
-					element = document.getElementForInsertionAt(selectionEnd);
+					selectionEnd = element.getEndOffset();
+					element = document.getElementForInsertionAt(selectionEnd + 1);
 				}
 
 			} else {