provide text lines with their sub-ranges for a given range of text
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/ContentTest.java b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/ContentTest.java
index 9a92e9d..bdb3982 100644
--- a/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/ContentTest.java
+++ b/org.eclipse.vex.core.tests/src/org/eclipse/vex/core/internal/dom/ContentTest.java
@@ -17,6 +17,7 @@
import org.eclipse.vex.core.provisional.dom.ContentRange;
import org.eclipse.vex.core.provisional.dom.IContent;
import org.eclipse.vex.core.provisional.dom.IPosition;
+import org.eclipse.vex.core.provisional.dom.MultilineText;
import org.junit.Before;
import org.junit.Test;
@@ -236,4 +237,41 @@
assertTrue("after", positionAfter.isValid());
}
+ @Test
+ public void givenTextWithLineBreaks_shouldProvideLinesWithRanges() throws Exception {
+ content.insertText(0, "line 1\nline 2\nline 3");
+
+ final MultilineText multilineText = content.getMultilineText(content.getRange());
+
+ assertEquals(3, multilineText.size());
+ assertEquals("line 1\n", multilineText.getText(0));
+ assertEquals(new ContentRange(0, 6), multilineText.getRange(0));
+ assertEquals("line 2\n", multilineText.getText(1));
+ assertEquals(new ContentRange(7, 13), multilineText.getRange(1));
+ assertEquals("line 3", multilineText.getText(2));
+ assertEquals(new ContentRange(14, 19), multilineText.getRange(2));
+ }
+
+ @Test
+ public void givenTextWithoutLineBreaks_shouldProvideSingleLineWithRange() throws Exception {
+ content.insertText(0, "line 1");
+
+ final MultilineText multilineText = content.getMultilineText(content.getRange());
+
+ assertEquals(1, multilineText.size());
+ assertEquals("line 1", multilineText.getText(0));
+ assertEquals(new ContentRange(0, 5), multilineText.getRange(0));
+ }
+
+ @Test
+ public void givenTextWithLineBreaks_whenTextEndsWithLineBreak_shouldNotProvideEmptyLineAsLastLine() throws Exception {
+ content.insertText(0, "line 1\nline 2\nline 3\n");
+
+ final MultilineText multilineText = content.getMultilineText(content.getRange());
+
+ assertEquals(3, multilineText.size());
+ assertEquals("line 3\n", multilineText.getText(2));
+ assertEquals(new ContentRange(14, 20), multilineText.getRange(2));
+ }
+
}
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/GapContent.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/GapContent.java
index 0b6b2c3..778f272 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/GapContent.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/GapContent.java
@@ -23,6 +23,7 @@
import org.eclipse.vex.core.provisional.dom.ContentRange;
import org.eclipse.vex.core.provisional.dom.IContent;
import org.eclipse.vex.core.provisional.dom.IPosition;
+import org.eclipse.vex.core.provisional.dom.MultilineText;
/**
* Implementation of the <code>Content</code> interface that manages changes efficiently. Implements a buffer that keeps
@@ -37,6 +38,7 @@
private static final float GROWTH_RATE_SLOW = 1.1f;
private static final char TAG_MARKER = '\0';
+ private static final char LINE_BREAK = '\n';
private char[] content;
private int gapStart;
@@ -152,6 +154,18 @@
return c == TAG_MARKER;
}
+ public boolean isLineBreak(final int offset) {
+ if (offset < 0 || offset >= length()) {
+ return false;
+ }
+
+ return isLineBreak(content[getIndex(offset)]);
+ }
+
+ private boolean isLineBreak(final char c) {
+ return c == LINE_BREAK;
+ }
+
@Override
public void remove(final ContentRange range) {
assertOffset(range.getStartOffset(), 0, length() - range.length());
@@ -240,6 +254,35 @@
}
@Override
+ public MultilineText getMultilineText(final ContentRange range) {
+ Assert.isTrue(getRange().contains(range));
+ final MultilineText result = new MultilineText();
+
+ StringBuilder currentLine = new StringBuilder();
+ int lineStart = range.getStartOffset();
+ for (int i = range.getStartOffset(); i <= range.getEndOffset(); i += 1) {
+ final char c = charAt(i);
+ if (isTagMarker(c)) {
+ // ignore tag markers
+ } else if (isLineBreak(c)) {
+ currentLine.append(c);
+ final ContentRange lineRange = new ContentRange(lineStart, i);
+ result.appendLine(currentLine.toString(), lineRange);
+ currentLine = new StringBuilder();
+ lineStart = i + 1;
+ } else {
+ currentLine.append(c);
+ }
+ }
+
+ if (currentLine.length() > 0) {
+ result.appendLine(currentLine.toString(), new ContentRange(lineStart, range.getEndOffset()));
+ }
+
+ return result;
+ }
+
+ @Override
public void insertContent(final int offset, final IContent content) {
assertOffset(offset, 0, length());
@@ -294,11 +337,7 @@
*/
@Override
public char charAt(final int offset) {
- if (offset < gapStart) {
- return content[offset];
- } else {
- return content[offset - gapStart + gapEnd];
- }
+ return content[getIndex(offset)];
}
/**
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IContent.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IContent.java
index aa396ae..18a0f38 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IContent.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/IContent.java
@@ -77,6 +77,8 @@
*/
String getRawText();
+ MultilineText getMultilineText(final ContentRange range);
+
/**
* Insert the given content into this content at the given offset.
*
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/MultilineText.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/MultilineText.java
new file mode 100644
index 0000000..120e70e
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/provisional/dom/MultilineText.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2015 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.provisional.dom;
+
+import java.util.ArrayList;
+
+public class MultilineText {
+
+ private final ArrayList<Line> lines = new ArrayList<Line>();
+
+ public void appendLine(final String text, final ContentRange range) {
+ lines.add(new Line(text, range));
+ }
+
+ public int size() {
+ return lines.size();
+ }
+
+ public String getText(final int lineIndex) {
+ final Line line = lines.get(lineIndex);
+ return line.text;
+ }
+
+ public ContentRange getRange(final int lineIndex) {
+ final Line line = lines.get(lineIndex);
+ return line.range;
+ }
+
+ private static class Line {
+ public final String text;
+ public final ContentRange range;
+
+ public Line(final String text, final ContentRange range) {
+ this.text = text;
+ this.range = range;
+ }
+ }
+}