Skip to content

Commit 05aa839

Browse files
fixup! Handle CR without NL printed in EditorConsole
1 parent 8fb9637 commit 05aa839

File tree

2 files changed

+168
-5
lines changed

2 files changed

+168
-5
lines changed

Diff for: app/src/processing/app/EditorConsole.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public class EditorConsole extends JScrollPane {
3939

4040
private static ConsoleOutputStream out;
4141
private static ConsoleOutputStream err;
42-
private static int startOfLine = 0;
43-
private static int insertPosition = 0;
42+
private int startOfLine = 0;
43+
private int insertPosition = 0;
4444

4545
private static synchronized void init(SimpleAttributeSet outStyle, PrintStream outStream, SimpleAttributeSet errStyle, PrintStream errStream) {
4646
if (out != null) {
@@ -185,21 +185,25 @@ public boolean isEmpty() {
185185
return document.getLength() == 0;
186186
}
187187

188+
// Regex for linesplitting, see insertString for comments.
189+
private static final Pattern newLinePattern = Pattern.compile("([^\r\n]*)([\r\n]*\n)?(\r+)?");
190+
188191
public void insertString(String str, SimpleAttributeSet attributes) throws BadLocationException {
189192
// Separate the string into content, newlines and lone carriage
190193
// returns.
191194
//
192-
// Doing so allows lone CRs to return the insertPosition to the
195+
// Doing so allows lone CRs to move the insertPosition back to the
193196
// start of the line to allow overwriting the most recent line (e.g.
194197
// for a progress bar). Any CR or NL that are immediately followed
195198
// by another NL are bunched together for efficiency, since these
196199
// can just be inserted into the document directly and still be
197200
// correct.
198201
//
199-
// This regex is written so it will necessarily match any string
202+
// The regex is written so it will necessarily match any string
200203
// completely if applied repeatedly. This is important because any
201204
// part not matched would be silently dropped.
202-
Matcher m = Pattern.compile("([^\r\n]*)([\r\n]*\n)?(\r+)?").matcher(str);
205+
Matcher m = newLinePattern.matcher(str);
206+
203207
while (m.find()) {
204208
String content = m.group(1);
205209
String newlines = m.group(2);

Diff for: app/test/processing/app/EditorConsoleTest.java

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Copyright 2020 Arduino LLC (http://www.arduino.cc/)
5+
*
6+
* Arduino is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
package processing.app;
31+
32+
import java.io.File;
33+
import javax.swing.SwingUtilities;
34+
35+
import static org.junit.Assert.assertEquals;
36+
37+
import org.junit.Before;
38+
import org.junit.BeforeClass;
39+
import org.junit.Test;
40+
41+
public class EditorConsoleTest extends AbstractWithPreferencesTest {
42+
EditorConsole console;
43+
44+
@Before
45+
public void createConsole() {
46+
console = new EditorConsole(null);
47+
}
48+
49+
public String escapeString(String input) {
50+
// This escapes backslashes, newlines and carriage returns, to get
51+
// more readable assertion failures.
52+
return input.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r");
53+
}
54+
55+
public void assertOutput(String output) {
56+
assertEquals(escapeString(output), escapeString(console.getText()));
57+
}
58+
59+
@Test
60+
public void testHelloWorld() throws Exception {
61+
console.insertString("Hello, world!", null);
62+
63+
assertOutput("Hello, world!");
64+
}
65+
66+
@Test
67+
public void testCrNlHandling() throws Exception {
68+
// Do some basic tests with \r\n
69+
console.insertString("abc\r\ndef", null);
70+
assertOutput("abc\r\ndef");
71+
72+
console.insertString("xyz", null);
73+
assertOutput("abc\r\ndefxyz");
74+
75+
console.insertString("000\r\n123", null);
76+
assertOutput("abc\r\ndefxyz000\r\n123");
77+
78+
console.insertString("\r\n", null);
79+
assertOutput("abc\r\ndefxyz000\r\n123\r\n");
80+
}
81+
82+
@Test
83+
public void testNlHandling() throws Exception {
84+
// Basic tests, but with just \n
85+
console.insertString("abc\ndef", null);
86+
assertOutput("abc\ndef");
87+
88+
console.insertString("xyz", null);
89+
assertOutput("abc\ndefxyz");
90+
91+
console.insertString("000\n123", null);
92+
assertOutput("abc\ndefxyz000\n123");
93+
94+
console.insertString("\n", null);
95+
assertOutput("abc\ndefxyz000\n123\n");
96+
}
97+
98+
@Test
99+
public void testCrHandling() throws Exception {
100+
// Then test that single \r clears the current line
101+
console.clear();
102+
console.insertString("abc\rdef", null);
103+
assertOutput("def");
104+
105+
// A single \r at the end is not added to the document
106+
console.insertString("\r", null);
107+
assertOutput("def");
108+
109+
// Nor are multiple \r at the end
110+
console.insertString("\r\r\r", null);
111+
assertOutput("def");
112+
113+
// But it does clear the line on the next write
114+
console.insertString("123", null);
115+
assertOutput("123");
116+
117+
// Same when combined with some data
118+
console.insertString("\r456\r\r", null);
119+
assertOutput("456");
120+
121+
console.insertString("000", null);
122+
assertOutput("000");
123+
124+
// Then add a newline so preceding data is kept
125+
console.insertString("\r\nxxx\r", null);
126+
assertOutput("000\r\nxxx");
127+
128+
// But data after the newline is removed
129+
console.insertString("yyy", null);
130+
assertOutput("000\r\nyyy");
131+
132+
// When a \r\n is split across inserts, it becomes a lone \n
133+
console.insertString("\r", null);
134+
assertOutput("000\r\nyyy");
135+
console.insertString("\n", null);
136+
assertOutput("000\r\nyyy\n");
137+
}
138+
139+
@Test
140+
public void testCrPartialOverwrite() throws Exception {
141+
console.insertString("abcdef\r", null);
142+
assertOutput("abcdef");
143+
144+
console.insertString("123", null);
145+
assertOutput("123def");
146+
147+
console.insertString("4", null);
148+
assertOutput("1234ef");
149+
150+
console.insertString("\r\n56", null);
151+
assertOutput("1234ef\r\n56");
152+
}
153+
154+
@Test
155+
public void testTogether() throws Exception {
156+
console.insertString("abc\n123456\rdef\rx\r\nyyy\nzzz\r999", null);
157+
assertOutput("abc\nxef456\r\nyyy\n999");
158+
}
159+
}

0 commit comments

Comments
 (0)