Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pazderski2019-10-05 11:13:57 +0000
committerPaul Pazderski2019-11-12 11:36:00 +0000
commitb61668507b32b353ea60da3fbd24bcbf7717afda (patch)
tree3776aa2c57cf003c58a2a24132c70efc52293ed0
parent0ad20a3402532969edd5cab954430b6b6f36f588 (diff)
downloadeclipse.platform.debug-I20191112-1800.tar.gz
eclipse.platform.debug-I20191112-1800.tar.xz
eclipse.platform.debug-I20191112-1800.zip
Bug 552014 - [console] Input problem with fixed width console and IMEI20191112-1800
When IME is started at end of fixed width line the first stroke is immediately inserted as character and the IME continued after. Change-Id: I9a197f49b9b9aae8c279fe5638fa5278d66294c8 Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java4
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleDocumentAdapterTests.java62
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleFixedWidthTests.java45
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTestUtil.java108
-rw-r--r--org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java4
-rw-r--r--org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleDocumentAdapter.java108
6 files changed, 256 insertions, 75 deletions
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
index d659beea9..9e3efa615 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java
@@ -19,6 +19,7 @@ import org.eclipse.debug.tests.breakpoint.BreakpointOrderingTests;
import org.eclipse.debug.tests.console.ConsoleDocumentAdapterTests;
import org.eclipse.debug.tests.console.ConsoleManagerTests;
import org.eclipse.debug.tests.console.ConsoleTests;
+import org.eclipse.debug.tests.console.IOConsoleFixedWidthTests;
import org.eclipse.debug.tests.console.IOConsoleTests;
import org.eclipse.debug.tests.console.ProcessConsoleManagerTests;
import org.eclipse.debug.tests.console.ProcessConsoleTests;
@@ -59,7 +60,7 @@ import junit.framework.TestSuite;
public class AutomatedSuite extends TestSuite {
/**
- * Returns the suite. This is required to use the JUnit Launcher.
+ * Returns the suite. This is required to use the JUnit Launcher.
*
* @return the test suite
*/
@@ -117,6 +118,7 @@ public class AutomatedSuite extends TestSuite {
addTest(new TestSuite(ConsoleManagerTests.class));
addTest(new TestSuite(ConsoleTests.class));
addTest(new TestSuite(IOConsoleTests.class));
+ addTest(new TestSuite(IOConsoleFixedWidthTests.class));
addTest(new TestSuite(ProcessConsoleManagerTests.class));
addTest(new TestSuite(ProcessConsoleTests.class));
addTest(new TestSuite(StreamsProxyTests.class));
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleDocumentAdapterTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleDocumentAdapterTests.java
index 02205f1d6..ffcd759c8 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleDocumentAdapterTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/ConsoleDocumentAdapterTests.java
@@ -199,7 +199,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
// add text starting at fixed width border
addText = "kl";
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 0, 1));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, docAdapter.getCharCount(), addText, 0, addText.length(), 0, 1));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(docAdapter.getCharCount(), 0, addText);
assertNumberOfLines(docAdapter, 5);
@@ -213,7 +213,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
// insert text starting at fixed width border
addText = ">";
offset = docAdapter.getCharCount() - 2;
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 1));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, addText, 0, addText.length(), 0, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, 0, addText);
assertNumberOfLines(docAdapter, 5);
@@ -225,7 +225,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
checkLineMapping(docAdapter, rand);
// delete character at fixed width border
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 1));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, "", 1, 0, 0, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, 1, "");
assertNumberOfLines(docAdapter, 5);
@@ -338,7 +338,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
docAdapter.replaceTextRange(offset, 0, addText);
replaceText = "uvwxyz_-=|";
offset = docAdapter.getCharCount() - 3;
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 1));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, replaceText, 3, replaceText.length(), 0, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, 3, replaceText);
assertNumberOfLines(docAdapter, 7);
@@ -357,7 +357,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
// remove last wrapped line
offset = docAdapter.getOffsetAtLine(docAdapter.getLineCount() - 1);
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 0));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, "", 10, 0, 1, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, 10, "");
assertNumberOfLines(docAdapter, 6);
@@ -376,7 +376,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
// remove 5th line including it's real line delimiter
offset = docAdapter.getOffsetAtLine(5 - 1);
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, null, null));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, "", 11, 0, 1, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, 11, "");
assertNumberOfLines(docAdapter, 5);
@@ -637,7 +637,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
// remove at fixed width border
offset = 10;
remove = 1;
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 0));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, "", remove, 0, 1, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, remove, "");
assertNumberOfLines(docAdapter, 1);
@@ -763,6 +763,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
assertLine(docAdapter, 3, "#B");
checkLineMapping(docAdapter, rand);
+ // replace last (real) line with new content
addText = "*+";
offset = docAdapter.getCharCount() - 2;
length = 1;
@@ -774,6 +775,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
assertLine(docAdapter, 3, "*+B");
checkLineMapping(docAdapter, rand);
+ // replace last character (at fixed width border) with new content
docAdapter.setText("");
docAdapter.replaceTextRange(docAdapter.getCharCount(), 0, "0123456789");
docAdapter.replaceTextRange(docAdapter.getCharCount(), 0, "ABCDEFGHIJ");
@@ -781,7 +783,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
addText = "$b";
offset = docAdapter.getCharCount() - 1;
length = 1;
- eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, null, null, null, null, 1, 1));
+ eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGING, offset, addText, length, addText.length(), 0, 0));
eventListener.addExpectation(new TextEventExpectation(TextChangeEventType.CHANGED));
docAdapter.replaceTextRange(offset, length, addText);
assertNumberOfLines(docAdapter, 3);
@@ -1164,6 +1166,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
* and {@link #docAdapter} is set do some additional validations.
*/
TextChangingEvent lastEvent;
+ int eventLineBeforeChange = -1;
int linesBeforeReplace = -1;
int lengthBeforeReplace = -1;
@@ -1180,31 +1183,30 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
@Override
public void textChanging(TextChangingEvent event) {
final TextEventExpectation expectation = checkCommon(TextChangeEventType.CHANGING);
- if (expectation == null) {
- return;
- }
-
- if (expectation.start != null) {
- assertEquals("event.start", (int) expectation.start, event.start);
- }
- if (expectation.newText != null) {
- assertEquals("event.newText", expectation.newText, event.newText);
- }
- if (expectation.replaceCharCount != null) {
- assertEquals("event.replaceCharCount", (int) expectation.replaceCharCount, event.replaceCharCount);
- }
- if (expectation.newCharCount != null) {
- assertEquals("event.newCharCount", (int) expectation.newCharCount, event.newCharCount);
- }
- if (expectation.replaceLineCount != null) {
- assertEquals("event.replaceLineCount", (int) expectation.replaceLineCount, event.replaceLineCount);
- }
- if (expectation.newLineCount != null) {
- assertEquals("event.newLineCount", (int) expectation.newLineCount, event.newLineCount);
+ if (expectation != null) {
+ if (expectation.start != null) {
+ assertEquals("event.start", (int) expectation.start, event.start);
+ }
+ if (expectation.newText != null) {
+ assertEquals("event.newText", expectation.newText, event.newText);
+ }
+ if (expectation.replaceCharCount != null) {
+ assertEquals("event.replaceCharCount", (int) expectation.replaceCharCount, event.replaceCharCount);
+ }
+ if (expectation.newCharCount != null) {
+ assertEquals("event.newCharCount", (int) expectation.newCharCount, event.newCharCount);
+ }
+ if (expectation.replaceLineCount != null) {
+ assertEquals("event.replaceLineCount", (int) expectation.replaceLineCount, event.replaceLineCount);
+ }
+ if (expectation.newLineCount != null) {
+ assertEquals("event.newLineCount", (int) expectation.newLineCount, event.newLineCount);
+ }
}
if (!allowUnexpectedEvents && docAdapter != null) {
lastEvent = event;
+ eventLineBeforeChange = docAdapter.getLineAtOffset(event.start);
linesBeforeReplace = docAdapter.getLineCount();
lengthBeforeReplace = docAdapter.getCharCount();
@@ -1223,7 +1225,7 @@ public class ConsoleDocumentAdapterTests extends AbstractDebugTest {
if (docAdapter != null && lastEvent != null) {
final int lastEventOffset = lastEvent.start;
final int lastEventLineIndex = docAdapter.getLineAtOffset(lastEventOffset);
- assertEquals("Line of offset " + lastEventOffset + " has changed after text change.", lastEventLineIndex, docAdapter.getLineAtOffset(lastEventOffset));
+ assertTrue("Line of event offset " + lastEventOffset + " has moved up.", eventLineBeforeChange <= lastEventLineIndex);
// check if predicted changes are correct
final int predictedDocLength = lengthBeforeReplace - lastEvent.replaceCharCount + lastEvent.newCharCount;
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleFixedWidthTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleFixedWidthTests.java
new file mode 100644
index 000000000..dba2f11cc
--- /dev/null
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleFixedWidthTests.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Paul Pazderski and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.tests.console;
+
+import org.eclipse.debug.tests.TestUtil;
+
+/**
+ * Same as {@link IOConsoleTests} but with fixed width console enabled.
+ */
+public class IOConsoleFixedWidthTests extends IOConsoleTests {
+
+ public IOConsoleFixedWidthTests() {
+ super(IOConsoleFixedWidthTests.class.getSimpleName());
+ }
+
+ public IOConsoleFixedWidthTests(String name) {
+ super(name);
+ }
+
+ @Override
+ protected IOConsoleTestUtil getTestUtil(String title) {
+ final IOConsoleTestUtil c = super.getTestUtil(title);
+ // Varying the width may reveal new bugs. There is no width value
+ // which is invalid. (but remember most test output is quite short)
+ // And try the most beautiful width of 1 aka the vertical console.
+ c.getConsole().setConsoleWidth(3);
+ c.setIgnoreFixedConsole(true);
+ // console width is applied asynchronous
+ TestUtil.waitForJobs(getName(), 50, 1000);
+ return c;
+ }
+
+ // the actual tests are inherited from IOConsoleTests
+}
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTestUtil.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTestUtil.java
index ee08f8105..ce67942cc 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTestUtil.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTestUtil.java
@@ -17,6 +17,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.tests.TestUtil;
import org.eclipse.jface.text.BadLocationException;
@@ -24,9 +25,11 @@ import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.Region;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ST;
import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.console.IConsoleDocumentPartitioner;
import org.eclipse.ui.console.IOConsole;
@@ -55,6 +58,24 @@ public final class IOConsoleTestUtil {
* initialized.
*/
private IOConsoleOutputStream defaultOut = null;
+ /**
+ * This utility was initial written to be used with disabled fixed width
+ * console. If fixed width is enabled some methods like 'set caret to line
+ * end' may behave unexpected if a test case was written with non fixed
+ * width console in mind.
+ * <p>
+ * If this field is set to <code>true</code> all methods which may behave
+ * problematic in fixed width are executed in a way so that there result is
+ * the same as if the console has no fixed width enabled. E.g. 'set caret to
+ * line end' ignores virtual line wraps and jumps to the next real line
+ * delimiter.
+ * </p>
+ * <p>
+ * If this field is set to <code>true</code> any test written for non fixed
+ * width mode should succeed regardless of the fixed width setting.
+ * </p>
+ */
+ private boolean ignoreFixedConsole = false;
/**
* Create a new testing helper.
@@ -304,7 +325,34 @@ public final class IOConsoleTestUtil {
*/
public IOConsoleTestUtil backspace(int repeat) {
for (int i = 0; i < repeat; i++) {
- textPanel.invokeAction(ST.DELETE_PREVIOUS);
+ if (ignoreFixedConsole) {
+ // Note: this backspace simulation can not handle multibyte line
+ // delimiter. It would only delete one byte with one backspace.
+ final Event e = new Event();
+ e.doit = true;
+ e.text = "";
+ final IRegion selection = getSelection();
+ if (selection.getLength() == 0) {
+ final int caretOffset = getCaretOffset();
+ e.start = caretOffset - 1;
+ e.end = caretOffset;
+ if (caretOffset <= 0) {
+ e.doit = false;
+ }
+ } else {
+ e.start = selection.getOffset();
+ e.end = selection.getOffset() + selection.getLength();
+ }
+ if (e.doit) {
+ textPanel.notifyListeners(SWT.Verify, e);
+ }
+ if (e.doit) {
+ textPanel.replaceTextRange(e.start, e.end - e.start, e.text);
+ setCaretOffset(e.start);
+ }
+ } else {
+ textPanel.invokeAction(ST.DELETE_PREVIOUS);
+ }
}
return this;
}
@@ -393,7 +441,17 @@ public final class IOConsoleTestUtil {
* @return this {@link IOConsoleTestUtil} to chain methods
*/
public IOConsoleTestUtil moveCaretToLineStart() {
- textPanel.invokeAction(ST.LINE_START);
+ if (ignoreFixedConsole) {
+ try {
+ final int currentOffset = getCaretOffset();
+ final int docLineStart = getDocument().getLineInformationOfOffset(currentOffset).getOffset();
+ setCaretOffset(docLineStart);
+ } catch (BadLocationException e) {
+ TestUtil.log(IStatus.ERROR, name, "Failed to set caret to line start in wrapped line mode.", e);
+ }
+ } else {
+ textPanel.invokeAction(ST.LINE_START);
+ }
return this;
}
@@ -403,11 +461,31 @@ public final class IOConsoleTestUtil {
* @return this {@link IOConsoleTestUtil} to chain methods
*/
public IOConsoleTestUtil moveCaretToLineEnd() {
- textPanel.invokeAction(ST.LINE_END);
+ if (ignoreFixedConsole) {
+ try {
+ final int currentOffset = getCaretOffset();
+ final IRegion docLine = getDocument().getLineInformationOfOffset(currentOffset);
+ setCaretOffset(docLine.getOffset() + docLine.getLength());
+ } catch (BadLocationException e) {
+ TestUtil.log(IStatus.ERROR, name, "Failed to set caret to line end in wrapped line mode.", e);
+ }
+ } else {
+ textPanel.invokeAction(ST.LINE_END);
+ }
return this;
}
/**
+ * Get the selected text region in console.
+ *
+ * @return the selected region
+ */
+ public IRegion getSelection() {
+ final Point selection = textPanel.getSelection();
+ return new Region(selection.x, selection.y - selection.x);
+ }
+
+ /**
* Select text in console.
*
* @param offset selection start position. May be negative to select
@@ -653,6 +731,30 @@ public final class IOConsoleTestUtil {
}
/**
+ * If <code>true</code> the util will work as if console is not in fixed
+ * width mode. E.g. {@link #moveCaretToLineStart()} will move caret to
+ * document line start not to widget line start.
+ *
+ * @see #ignoreFixedConsole
+ */
+ public boolean isIgnoreFixedConsole() {
+ return ignoreFixedConsole;
+ }
+
+ /**
+ * Enable compatibility mode. If set to <code>true</code> written for
+ * console without fixed width should work with any fixed width. Commands
+ * like {@link #moveCaretToLineStart()} are modified to not move to begin of
+ * widget line (maybe wrapped line) but to start it would have without fixed
+ * width.
+ *
+ * @see #ignoreFixedConsole
+ */
+ public void setIgnoreFixedConsole(boolean ignoreWrappeding) {
+ this.ignoreFixedConsole = ignoreWrappeding;
+ }
+
+ /**
* Get identifier for output {@link IOConsolePartition}s.
*
* @return output partition identifier
diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java
index ce203b915..e244d2fbb 100644
--- a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java
+++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java
@@ -121,7 +121,7 @@ public class IOConsoleTests extends AbstractDebugTest {
* @param title console title
* @return util to help testing console functions
*/
- private IOConsoleTestUtil getTestUtil(String title) {
+ protected IOConsoleTestUtil getTestUtil(String title) {
final IOConsole console = new IOConsole(title, "", null, StandardCharsets.UTF_8.name(), true);
consoleFinished.set(false);
console.addPropertyChangeListener((PropertyChangeEvent event) -> {
@@ -150,7 +150,7 @@ public class IOConsoleTests extends AbstractDebugTest {
* @param c the test util containing the console to close
* @param expected content this {@link IOConsole} input stream has received
*/
- private void closeConsole(IOConsoleTestUtil c, String... expectedInputLines) throws IOException {
+ protected void closeConsole(IOConsoleTestUtil c, String... expectedInputLines) throws IOException {
if (consoleFinished.get()) {
// This should only happen if no output streams where used and the
// user input stream was explicit closed before
diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleDocumentAdapter.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleDocumentAdapter.java
index 88bc3197c..89a545a95 100644
--- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleDocumentAdapter.java
+++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleDocumentAdapter.java
@@ -135,6 +135,47 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
private int[] widgetLineOffsets = new int[1];
/**
+ * The fact that wrapped lines are new lines without a newline delimiter leads
+ * to some hard to handle edge cases since all involved interfaces (implicit)
+ * assume a newline has its unique offset.
+ * <p>
+ * Consider a fixed with of 10 and a console filled with (automatically wrapped)
+ * content of:
+ * </p>
+ *
+ * <pre>
+ * 0123456789
+ * 0
+ * </pre>
+ *
+ * <p>
+ * If we remove the last character ('0') we must set replaceLineCount to 1 since
+ * there is one line less due to the unwrapped line. The replaceLineCount is
+ * necessary so that StyledTextRenderer updates the lines which possible follow
+ * below and have changed due to moved content.
+ * </p>
+ * <p>
+ * But StyledTextRenderer presumes that the line index where the event occurs is
+ * the same before and after the text change. This is not the case for our auto
+ * wrapped lines since getLineAtOffset(10) is 1 before text change and 0 after
+ * </p>
+ * <p>
+ * To solve this unlucky situation we will lie to StyledText(Renderer) for the
+ * short time between text changing event and actual text change. If we know the
+ * same (start) offset will yield a different line index before and after change
+ * we will return for this offset the line index it will have after the change
+ * already before the change is applied. In this example if StyledText ask for
+ * getLineAtOffset(10) we return 0 already before the text is actual changed.
+ * </p>
+ * <p>
+ * This field store the offset for which we should preempt the new line index.
+ * It must be an offset at fixed width border (or negative for none) because
+ * only those have the potential for two different line indexes at same offset.
+ * </p>
+ */
+ private int preemptLineWrapChange = -1;
+
+ /**
* New {@link ConsoleDocumentAdapter} with no {@link IDocument} connected yet.
*
* @param width fixed console width to enforce text wrap or &lt;= 0 to disable
@@ -264,6 +305,13 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
// is the index before the insertion index.
widgetLine = (-widgetLine) - 2;
}
+ if (offset == preemptLineWrapChange) {
+ // The requested offset is at fixed width border. In some text change situations
+ // we must return the line index it will have after the change even if the
+ // change is not applied yet. See #preemptLineWrapChange Javadoc for more
+ // details.
+ widgetLine--;
+ }
return widgetLine;
}
}
@@ -385,10 +433,10 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
* @throws BadLocationException if document event is invalid
*/
private TextChangingEvent generateTextChangingEvent(DocumentEvent event) throws BadLocationException {
- String newText = event.getText() == null ? "" : event.getText(); //$NON-NLS-1$
- int newTextLength = newText.length();
- int eventOffset = event.getOffset();
- int eventLength = event.getLength();
+ final String newText = event.getText() == null ? "" : event.getText(); //$NON-NLS-1$
+ final int newTextLength = newText.length();
+ final int eventOffset = event.getOffset();
+ final int eventLength = event.getLength();
int replaceLineCount = 0;
int newLineCount = 0;
@@ -400,45 +448,14 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
// single character inserted at first offset can, from StyledText-Widgets
// perspective, change the content of every line.
- if (newTextLength >= 0 || eventLength > 0) {
+ if (newTextLength > 0 || eventLength > 0) {
// In this method first and last refer to the first and last line affected by
// the current document event
- final int firstDocLineIndex = document.getLineOfOffset(eventOffset);
- final int firstDocLineOffset = document.getLineOffset(firstDocLineIndex);
-
- if (eventOffset != firstDocLineOffset && (eventOffset - firstDocLineOffset) % fixedConsoleWidth == 0) {
- // event start is at fixed width border
- // XXX: the trick here is important to do the impossible
- // The fact that wrapped lines are new lines without a newline delimiter leads
- // to some (nearly) impossible edge cases since all involved interfaces
- // (implicit) assume a newline has its unique offset.
- //
- // Consider a fixed with of 10 and a console filled with (automatically wrapped)
- // content of:
- // 0123456789
- // 0
- // If we remove the last character ('0') we must set replaceLineCount to 1 since
- // there is one line less due to the unwrapped line. The replaceLineCount is
- // necessary so that StyledTextRenderer updates the lines which possible follow
- // below and have changed due to moved content.
- // But StyledTextRenderer presumes that the line index where the event occurs is
- // the same before and after the text change. This is not the case for our auto
- // wrapped lines since getLineAtOffset(10) is 1 before text change and 0 after.
- //
- // To solve this unlucky situation we simply never send text change event
- // occurring at the fixed width wrap border. If such an document change happens
- // we expand the text change to include also the character before the wrap
- // border.
- eventOffset--;
- eventLength++;
- newText = document.get(eventOffset, 1) + newText;
- newTextLength++;
- }
-
final int eventEnd = eventOffset + eventLength;
final int firstWidgetLineIndex = getLineOfOffset(eventOffset);
final int firstWidgetLineOffset = getLineOffset(firstWidgetLineIndex);
+ final int firstInsertLength;
final int lastInsertLength;
int lastDocLineLengthDiff = -eventLength;
@@ -446,6 +463,7 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
Match newLineMatch = docLegalLineDelimiterMatcher.indexOf(newText, newTextOffset);
if (newLineMatch == null) {
// single line insert
+ firstInsertLength = newTextLength;
lastInsertLength = eventOffset - firstWidgetLineOffset + newTextLength;
lastDocLineLengthDiff += newTextLength;
} else {
@@ -458,7 +476,7 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
// 3. Last line: everything (including) last line delimiter to end of inserted
// text
- final int firstInsertLength = newLineMatch.getOffset();
+ firstInsertLength = newLineMatch.getOffset();
// newLineCount here is numbers of lines required if text is wrapped -1 because
// we start inserting in an existing line and +1 for the first line delimiter we
// had found
@@ -512,6 +530,18 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
lastAffectedWidgetLineIndex--;
}
replaceLineCount = lastAffectedWidgetLineIndex - firstWidgetLineIndex;
+
+ if (firstInsertLength == 0 && eventLength > 0 && affectedContentAfterRange == 0) {
+ final int firstDocLineOffset = document.getLineInformationOfOffset(eventOffset).getOffset();
+ if (eventOffset != firstDocLineOffset && (eventOffset - firstDocLineOffset) % fixedConsoleWidth == 0) {
+ // Text change produce a tricky wrapped line change. Change start at fixed width
+ // border and is at start of wrapped line because there is wrapped content after
+ // event start. After change there is no more wrapped content after so the same
+ // event start offset is now one widget line above.
+ replaceLineCount++;
+ preemptLineWrapChange = eventOffset;
+ }
+ }
}
final TextChangingEvent changingEvent = new TextChangingEvent(this);
@@ -550,7 +580,7 @@ public class ConsoleDocumentAdapter implements IDocumentAdapter, IDocumentListen
@Override
public synchronized void documentChanged(DocumentEvent event) {
-
+ preemptLineWrapChange = -1;
updateWidgetOffsets(event.getOffset());
TextChangedEvent changeEvent = new TextChangedEvent(this);

Back to the top