show the content around the caret in the info panel of BoxDemoView

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/core/TextUtils.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/core/TextUtils.java
index 0ac3710..9b0f23f 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/core/TextUtils.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/core/TextUtils.java
@@ -16,7 +16,11 @@
 
 public class TextUtils {
 
-	private static final Pattern ANY_LINE_BREAKS = Pattern.compile("(\r\n|\r|\n)");
+	public static final char CURRENCY_SIGN = '\u00A4';
+	public static final char PARAGRAPH_SIGN = '\u00B6';
+	public static final char RAQUO = '\u00BB';
+
+	public static final Pattern ANY_LINE_BREAKS = Pattern.compile("(\r\n|\r|\n)");
 
 	public static int countWhitespaceAtStart(final String text) {
 		int whitespaceCount = 0;
@@ -46,5 +50,4 @@
 	public static String[] lines(final String s) {
 		return ANY_LINE_BREAKS.split(s, -1);
 	}
-
 }
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/ContentRange.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/ContentRange.java
index 3b59ee9..1336774 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/ContentRange.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/ContentRange.java
@@ -52,6 +52,20 @@
 		}

 	}

 

+	/**

+	 * Create a ContentRange around the given offset with the given window size in each direction. The length of the

+	 * created range is twice the given window size.

+	 *

+	 * @param offset

+	 *            the offset

+	 * @param windowSize

+	 *            the number of characters to include on each side of offset

+	 * @return the range around offset

+	 */

+	public static ContentRange window(final int offset, final int windowSize) {

+		return new ContentRange(offset - windowSize, offset + windowSize);

+	}

+

 	public int getStartOffset() {

 		return startOffset;

 	}

@@ -131,6 +145,15 @@
 		return new ContentRange(startOffset + deltaStart, endOffset + deltaEnd);

 	}

 

+	/**

+	 * Resize this range to fit into the given limiting range.

+	 *

+	 * @return the resized range

+	 */

+	public ContentRange limitTo(final ContentRange limitingRange) {

+		return new ContentRange(Math.max(limitingRange.startOffset, startOffset), Math.min(endOffset, limitingRange.endOffset));

+	}

+

 	@Override

 	public String toString() {

 		return "ContentRange[" + startOffset + ", " + endOffset + "]";

diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxDemoView.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxDemoView.java
index 7898aec..71288b7 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxDemoView.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxDemoView.java
@@ -10,6 +10,11 @@
  *******************************************************************************/
 package org.eclipse.vex.ui.boxview;
 
+import static org.eclipse.vex.core.internal.core.TextUtils.ANY_LINE_BREAKS;
+import static org.eclipse.vex.core.internal.core.TextUtils.CURRENCY_SIGN;
+import static org.eclipse.vex.core.internal.core.TextUtils.PARAGRAPH_SIGN;
+import static org.eclipse.vex.core.internal.core.TextUtils.RAQUO;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -46,6 +51,9 @@
 import org.eclipse.vex.core.internal.widget.DOMController;
 import org.eclipse.vex.core.internal.widget.swt.BoxWidget;
 import org.eclipse.vex.core.internal.widget.swt.IVexSelection;
+import org.eclipse.vex.core.provisional.dom.ContentRange;
+import org.eclipse.vex.core.provisional.dom.IContent;
+import org.eclipse.vex.core.provisional.dom.IDocument;
 import org.w3c.css.sac.CSSException;
 import org.w3c.css.sac.InputSource;
 
@@ -56,19 +64,21 @@
  */
 public class BoxDemoView extends ViewPart {
 
+	private static final int CONTEXT_WINDOW = 5;
 	private static final int SAMPLE_COUNT = 25;
 	private static final IPath CSS_WORKSPACE_FILE = new Path("/test/box-demo.css");
 
 	private Composite boxWidgetParent;
 	private BoxWidget boxWidget;
-
+	private IDocument document;
+	
 	private Label offsetLabel;
+	private Label contextLabel;
 
 	private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
 		@Override
 		public void selectionChanged(final SelectionChangedEvent event) {
-			final ISelection selection = event.getSelection();
-			offsetLabel.setText(caretPositionAsText(selection));
+			updateInfoPanel(event.getSelection());
 		}
 	};
 
@@ -112,9 +122,13 @@
 		infoPanel.setLayout(new RowLayout());
 
 		new Label(infoPanel, SWT.NONE).setText("Caret Position:");
-		offsetLabel = new Label(infoPanel, SWT.RIGHT);
+		offsetLabel = new Label(infoPanel, SWT.LEFT);
 		offsetLabel.setLayoutData(new RowData(40, SWT.DEFAULT));
 
+		new Label(infoPanel, SWT.NONE).setText("Caret Context:");
+		contextLabel = new Label(infoPanel, SWT.LEFT);
+		contextLabel.setLayoutData(new RowData(SWT.DEFAULT, SWT.DEFAULT));
+
 		recreateBoxWidget();
 	}
 
@@ -139,12 +153,13 @@
 		}
 		boxWidget = new BoxWidget(boxWidgetParent, SWT.V_SCROLL);
 
-		boxWidget.setContent(UniversalTestDocument.createTestDocumentWithInlineElements(SAMPLE_COUNT));
+		document = UniversalTestDocument.createTestDocumentWithInlineElements(SAMPLE_COUNT);
+		boxWidget.setContent(document);
 		boxWidget.setBoxModelBuilder(new CSSBasedBoxModelBuilder(readStyleSheet()));
 		boxWidgetParent.layout();
 		boxWidget.addSelectionChangedListener(selectionChangedListener);
 
-		offsetLabel.setText(caretPositionAsText(boxWidget.getSelection()));
+		updateInfoPanel(boxWidget.getSelection());
 	}
 
 	private void cleanStaleReferenceInShell() {
@@ -155,6 +170,29 @@
 		boxWidgetParent.setFocus();
 	}
 
+	private void updateInfoPanel(final ISelection selection) {
+		final int caretPosition = caretPosition(selection);
+		offsetLabel.setText(Integer.toString(caretPosition));
+		contextLabel.setText(caretContext(caretPosition, document.getContent()));
+		contextLabel.getParent().layout();
+	}
+
+	private static int caretPosition(final ISelection selection) {
+		return ((IVexSelection) selection).getCaretOffset();
+	}
+
+	private static String caretContext(final int caretPosition, final IContent content) {
+		final ContentRange contextRange = ContentRange.window(caretPosition, CONTEXT_WINDOW).limitTo(content.getRange());
+		final String rawContext = content.getRawText(contextRange);
+		final int caretIndexInText = caretPosition - contextRange.getStartOffset();
+
+		final String caretContext = (rawContext.substring(0, caretIndexInText) + "|" + rawContext.substring(caretIndexInText))
+				.replaceAll(ANY_LINE_BREAKS.pattern(), Character.toString(PARAGRAPH_SIGN))
+				.replaceAll("\0", Character.toString(CURRENCY_SIGN))
+				.replaceAll("\t", Character.toString(RAQUO));
+		return caretContext;
+	}
+
 	private void reloadStyleSheet() {
 		boxWidget.setBoxModelBuilder(new CSSBasedBoxModelBuilder(readStyleSheet()));
 		rebuildBoxModel();
@@ -184,10 +222,6 @@
 		return null;
 	}
 
-	private static String caretPositionAsText(final ISelection selection) {
-		return Integer.toString(((IVexSelection) selection).getCaretOffset());
-	}
-
 	public void rebuildBoxModel() {
 		final DOMController controller = boxWidget.getDOMController();
 		controller.rebuildBoxModel();