Skip to content

Commit 65211f2

Browse files
committed
Add hard wrap limit option to console
This change adds the following options to org.eclipse.debug.ui: * Console.limitLongLines (boolean) * Console.limitLongLinesWrap (boolean) * Console.limitLongLinesLength (int) When limitLongLines is set, long console lines will be trimmed or wrapped. If limitLongLinesWrap is set, lines are wrapped. Otherwies lines are trimmed. The length at which long lines are trimmed/wrapped is limitLongLinesLength. The preferences apply to output before its appended to the console document. In contrast, the existing console word wrapping is applied after the output is appended to the console document. Fixes: #2479
1 parent 0eda9f4 commit 65211f2

16 files changed

Lines changed: 595 additions & 4 deletions

File tree

debug/org.eclipse.debug.tests/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ Export-Package: org.eclipse.debug.tests,
3131
Import-Package: org.assertj.core.api;version="3.24.2",
3232
org.assertj.core.api.iterable,
3333
org.junit.jupiter.api;version="[5.14.0,6.0.0)",
34-
org.junit.jupiter.api.io;version="[5.14.0,6.0.0)",
3534
org.junit.jupiter.api.extension;version="[5.14.0,6.0.0)",
3635
org.junit.jupiter.api.function;version="[5.14.0,6.0.0)",
36+
org.junit.jupiter.api.io;version="[5.14.0,6.0.0)",
3737
org.junit.jupiter.params;version="[5.14.0,6.0.0)",
38+
org.junit.jupiter.params.provider;version="[5.14.0,6.0.0)",
3839
org.junit.platform.suite.api;version="[1.14.0,2.0.0)",
3940
org.opentest4j;version="[1.3.0,2.0.0)"
4041
Eclipse-BundleShape: dir

debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/AutomatedSuite.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
import org.eclipse.debug.tests.breakpoint.BreakpointTests;
2020
import org.eclipse.debug.tests.breakpoint.SerialExecutorTest;
2121
import org.eclipse.debug.tests.console.ConsoleDocumentAdapterTests;
22-
import org.eclipse.debug.tests.console.ConsoleShowHideTests;
2322
import org.eclipse.debug.tests.console.ConsoleManagerTests;
23+
import org.eclipse.debug.tests.console.ConsoleOutputLineTruncateTest;
24+
import org.eclipse.debug.tests.console.ConsoleOutputLineWrapTest;
25+
import org.eclipse.debug.tests.console.ConsoleShowHideTests;
2426
import org.eclipse.debug.tests.console.ConsoleTests;
2527
import org.eclipse.debug.tests.console.FileLinkTests;
2628
import org.eclipse.debug.tests.console.IOConsoleFixedWidthTests;
@@ -122,6 +124,8 @@
122124
ConsoleManagerTests.class, //
123125
ConsoleTests.class, //
124126
IOConsoleTests.class, //
127+
ConsoleOutputLineWrapTest.class, //
128+
ConsoleOutputLineTruncateTest.class, //
125129
IOConsoleFixedWidthTests.class, //
126130
ProcessConsoleManagerTests.class, //
127131
ProcessConsoleTests.class, //
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Simeon Andreev and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Simeon Andreev - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.debug.tests.console;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
18+
import java.util.Arrays;
19+
import java.util.stream.Stream;
20+
21+
import org.eclipse.ui.internal.console.ConsoleOutputLineTruncate;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.Arguments;
24+
import org.junit.jupiter.params.provider.MethodSource;
25+
26+
public class ConsoleOutputLineTruncateTest {
27+
28+
private static final String NL = System.lineSeparator();
29+
30+
record C(int n, int m, int r, String i, String o) {
31+
}
32+
33+
private static C test(int limit, String input, String output) {
34+
return test(limit, 1, 1, input, output);
35+
}
36+
37+
private static C test(int limit, int chunks, int repeat, String input, String output) {
38+
return new C(limit, chunks, repeat, input, output);
39+
}
40+
41+
private static final C[] TESTS = {
42+
test(10, "========", "========"),
43+
test(10, 10, 1, "========", "========== ...\n"),
44+
test(4, "========", "==== ...\n"),
45+
test(4, 10, 1, "========", "==== ...\n"),
46+
test(4, "====\n====", "====\n===="),
47+
test(4, 5, 1, "====\n====", "====\n==== ...\n==== ...\n==== ...\n==== ...\n===="),
48+
49+
test(2, 5, 1, "=======\n==", "== ...\n== ...\n== ...\n== ...\n== ...\n=="),
50+
test(2, "====\n====", "== ...\n== ...\n"),
51+
test(2, 5, 1, "====\n====", "== ...\n== ...\n== ...\n== ...\n== ...\n== ...\n"),
52+
test(2, "=========", "== ...\n"),
53+
test(2, "=======\n==", "== ...\n=="),
54+
test(3, "=========", "=== ...\n"),
55+
test(3, 5, 1, "=========", "=== ...\n"),
56+
test(3, "========\n=", "=== ...\n="),
57+
58+
test(2, "======\n======", "== ...\n== ...\n"),
59+
test(2, 5, 1, "======\n======", "== ...\n== ...\n== ...\n== ...\n== ...\n== ...\n"),
60+
61+
test(3, 3, 1, "========\n=", "=== ...\n=== ...\n=== ...\n="),
62+
test(4, 3, 1, "========\n=", "==== ...\n==== ...\n==== ...\n="), };
63+
64+
private static Stream<Arguments> tests() {
65+
return Arrays.stream(TESTS).map(Arguments::of);
66+
}
67+
68+
@ParameterizedTest
69+
@MethodSource("tests")
70+
public void test(C p) {
71+
String input = p.i.replaceAll("\n", NL);
72+
String output = p.o.replaceAll("\n", NL);
73+
ConsoleOutputLineTruncate truncate = new ConsoleOutputLineTruncate();
74+
StringBuilder c = new StringBuilder();
75+
for (int i = 0; i < p.m; ++i) {
76+
StringBuilder s = new StringBuilder(input);
77+
CharSequence text = truncate.modify(s, p.n);
78+
c.append(text);
79+
}
80+
String expected = repeat(output, p.r);
81+
assertEquals(expected, c.toString());
82+
}
83+
84+
private static String repeat(String s, int r) {
85+
return (NL + s).repeat(r).substring(NL.length());
86+
}
87+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Simeon Andreev and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Simeon Andreev - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.debug.tests.console;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
18+
import java.util.Arrays;
19+
import java.util.stream.Stream;
20+
21+
import org.eclipse.ui.internal.console.ConsoleOutputLineWrap;
22+
import org.junit.jupiter.params.ParameterizedTest;
23+
import org.junit.jupiter.params.provider.Arguments;
24+
import org.junit.jupiter.params.provider.MethodSource;
25+
26+
public class ConsoleOutputLineWrapTest {
27+
28+
private static final String NL = System.lineSeparator();
29+
30+
record C (int n, int m, int r, String i, String o) {}
31+
32+
private static C test(int limit, String input, String output) {
33+
return test(limit, 1, 1, input, output);
34+
}
35+
36+
private static C test(int limit, int chunks, String input, String output) {
37+
return test(limit, chunks, chunks, input, output);
38+
}
39+
40+
private static C test(int limit, int chunks, int repeat, String input, String output) {
41+
return new C (limit, chunks, repeat, input, output);
42+
}
43+
44+
private static final C[] TESTS = {
45+
test(10, "========" , "========" ),
46+
test(10, 10, 8, "========" , "=========="),
47+
test( 4, "========" , "====\n===="),
48+
test( 4, 10, "========" , "====\n===="),
49+
test( 4, "====\n====" , "====\n===="),
50+
test( 4, 10, "====\n====" , "====\n===="),
51+
52+
test( 2, 10, "=======\n==" , "==\n==\n==\n=\n=="),
53+
test( 2, "====\n====" , "==\n==\n==\n==" ),
54+
test( 2, 10, "====\n====" , "==\n==\n==\n==" ),
55+
test( 2, "=========" , "==\n==\n==\n==\n="),
56+
test( 2, "=======\n==" , "==\n==\n==\n=\n=="),
57+
test( 3, "=========" , "===\n===\n===" ),
58+
test( 3, 10, "=========" , "===\n===\n===" ),
59+
test( 3, "========\n=" , "===\n===\n==\n=" ),
60+
61+
test( 2, "======\n======", "==\n==\n==\n==\n==\n=="),
62+
test( 2, 10, "======\n======", "==\n==\n==\n==\n==\n=="),
63+
64+
test( 3, 3, 1, "========\n=" , "===\n===\n==\n===\n===\n===\n===\n===\n===\n="),
65+
test( 4, 3, 1, "========\n=" , "====\n====\n====\n====\n=\n====\n====\n=\n=" ),
66+
};
67+
68+
private static Stream<Arguments> tests() {
69+
return Arrays.stream(TESTS).map(Arguments::of);
70+
}
71+
72+
@ParameterizedTest
73+
@MethodSource("tests")
74+
public void test(C p) {
75+
String input = p.i.replaceAll("\n", NL);
76+
String output = p.o.replaceAll("\n", NL);
77+
ConsoleOutputLineWrap lineBreak = new ConsoleOutputLineWrap();
78+
StringBuilder c = new StringBuilder();
79+
for (int i = 0; i < p.m; ++i) {
80+
StringBuilder s = new StringBuilder(input);
81+
CharSequence text = lineBreak.modify(s, p.n);
82+
c.append(text);
83+
}
84+
String expected = repeat(output, p.r);
85+
assertEquals(expected, c.toString());
86+
}
87+
88+
private static String repeat(String s, int r) {
89+
return (NL + s).repeat(r).substring(NL.length());
90+
}
91+
}

debug/org.eclipse.debug.tests/src/org/eclipse/debug/tests/console/IOConsoleTests.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,122 @@ public void testTrimSurrogateCharacters() throws Exception {
761761
}
762762
}
763763

764+
/**
765+
* Check trimming of long lines.
766+
*/
767+
@Test
768+
public void testTrimLongLine() throws Exception {
769+
final IOConsoleTestUtil c = getTestUtil("Test trim long line");
770+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
771+
c.getConsole().setLimitLineLength(false, 8);
772+
c.writeFast("first\n");
773+
c.writeFast("0123456789\n", out);
774+
c.write("last\n");
775+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
776+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
777+
c.waitForScheduledJobs();
778+
c.verifyContent("first\n01234567 ...\nlast\n");
779+
closeConsole(c);
780+
}
781+
}
782+
783+
/**
784+
* Check that trimming long lines doesn't split '\r\n'.
785+
*/
786+
@Test
787+
public void testTrimLongLineNewline() throws Exception {
788+
final IOConsoleTestUtil c = getTestUtil("Test trim long line");
789+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
790+
c.getConsole().setLimitLineLength(false, 8);
791+
c.writeFast("first\n");
792+
c.writeFast("0123456\r\n", out);
793+
c.write("last\n");
794+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
795+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
796+
c.waitForScheduledJobs();
797+
c.verifyContent("first\n0123456\r\nlast\n");
798+
closeConsole(c);
799+
}
800+
}
801+
802+
/**
803+
* Check that trimming long lines doesn't split surrogate pairs, e.g.
804+
* emojis.
805+
*/
806+
@Test
807+
public void testTrimLongLineSurrogateCharacters() throws Exception {
808+
final IOConsoleTestUtil c = getTestUtil("Test trim long line");
809+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
810+
c.getConsole().setLimitLineLength(false, 8);
811+
c.writeFast("first\n");
812+
c.writeFast("01234😀😀😀\n", out);
813+
c.write("last\n");
814+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
815+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
816+
c.waitForScheduledJobs();
817+
c.verifyContent("first\n01234😀 ...\nlast\n");
818+
closeConsole(c);
819+
}
820+
}
821+
822+
/**
823+
* Check wrapping of long lines.
824+
*/
825+
@Test
826+
public void testWrapLongLine() throws Exception {
827+
final IOConsoleTestUtil c = getTestUtil("Test wrap long line");
828+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
829+
c.getConsole().setLimitLineLength(true, 8);
830+
c.writeFast("first\n");
831+
c.writeFast("0123456789\n", out);
832+
c.write("last\n");
833+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
834+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
835+
c.waitForScheduledJobs();
836+
c.verifyContent("first\n01234567\n89\nlast\n");
837+
closeConsole(c);
838+
}
839+
}
840+
841+
/**
842+
* Check that wrapping long lines doesn't split '\r\n'.
843+
*/
844+
@Test
845+
public void testWrapLongLineNewline() throws Exception {
846+
final IOConsoleTestUtil c = getTestUtil("Test trim long line");
847+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
848+
c.getConsole().setLimitLineLength(true, 8);
849+
c.writeFast("first\n");
850+
c.writeFast("0123456\r\n", out);
851+
c.write("last\n");
852+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
853+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
854+
c.waitForScheduledJobs();
855+
c.verifyContent("first\n0123456\r\nlast\n");
856+
closeConsole(c);
857+
}
858+
}
859+
860+
/**
861+
* Check that wrapping long lines doesn't split surrogate pairs, e.g.
862+
* emojis.
863+
*/
864+
@Test
865+
public void testWrapLongLineSurrogateCharacters() throws Exception {
866+
final IOConsoleTestUtil c = getTestUtil("Test trim long line");
867+
try (IOConsoleOutputStream out = c.getDefaultOutputStream()) {
868+
c.getConsole().setLimitLineLength(true, 8);
869+
c.writeFast("first\n");
870+
c.writeFast("0123456😀😀\n", out);
871+
c.write("last\n");
872+
c.verifyContentByLine("first", 0).verifyContentByLine("last", -2);
873+
assertTrue(c.getDocument().getNumberOfLines() > 2, "Document not filled.");
874+
c.waitForScheduledJobs();
875+
c.verifyContent("first\n0123456\n😀😀\nlast\n");
876+
closeConsole(c);
877+
}
878+
}
879+
764880
/**
765881
* Some extra tests for IOConsolePartitioner.
766882
*/

debug/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/DebugUIPreferenceInitializer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ public void initializeDefaultPreferences() {
8686
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_LIMIT_CONSOLE_OUTPUT, true);
8787
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_LOW_WATER_MARK, 80000);
8888
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_HIGH_WATER_MARK, 100000);
89+
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_LIMIT_LINES, false);
90+
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_LIMIT_LINES_WRAP, false);
91+
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_LIMIT_LINES_LENGTH, 1000);
8992
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_TAB_WIDTH, 8);
9093
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_INTERPRET_CONTROL_CHARACTERS, false);
9194
prefs.setDefault(IDebugPreferenceConstants.CONSOLE_INTERPRET_CR_AS_CONTROL_CHARACTER, true);

0 commit comments

Comments
 (0)