Skip to content

Commit d733739

Browse files
authoredDec 21, 2023
Provide a new Terminal InputFlag INORMEOL to normalize end of lines (#900, fixes #899)
1 parent af7777d commit d733739

File tree

7 files changed

+112
-7
lines changed

7 files changed

+112
-7
lines changed
 

‎reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java

+39
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
*/
99
package org.jline.terminal.impl;
1010

11+
import java.io.BufferedReader;
1112
import java.io.ByteArrayInputStream;
1213
import java.io.ByteArrayOutputStream;
1314
import java.io.FilterInputStream;
1415
import java.io.IOException;
1516
import java.io.InputStream;
17+
import java.io.InputStreamReader;
1618
import java.io.PipedInputStream;
1719
import java.io.PipedOutputStream;
1820
import java.nio.charset.StandardCharsets;
@@ -51,6 +53,43 @@ public void tearDown() {
5153
System.clearProperty(TerminalBuilder.PROP_PROVIDERS);
5254
}
5355

56+
@Test
57+
void testEOL() throws IOException {
58+
{
59+
PipedInputStream in = new PipedInputStream();
60+
PipedOutputStream outIn = new PipedOutputStream(in);
61+
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
62+
outIn.write("abc\rdef\nghi\r\njkl\r".getBytes());
63+
64+
assertEquals("abc", reader.readLine());
65+
assertEquals("def", reader.readLine());
66+
assertEquals("ghi", reader.readLine());
67+
assertEquals("jkl", reader.readLine());
68+
}
69+
{
70+
PipedInputStream in = new PipedInputStream();
71+
PipedOutputStream outIn = new PipedOutputStream(in);
72+
ByteArrayOutputStream out = new ByteArrayOutputStream();
73+
outIn.write("abc\rdef\nghi\r\njkl\n".getBytes());
74+
75+
Terminal terminal = TerminalBuilder.builder()
76+
.type("ansi")
77+
.streams(in, out)
78+
.paused(true)
79+
.build();
80+
LineReader reader = LineReaderBuilder.builder().terminal(terminal).build();
81+
Attributes attributes = terminal.getAttributes();
82+
attributes.setInputFlag(InputFlag.INORMEOL, true);
83+
terminal.setAttributes(attributes);
84+
terminal.resume();
85+
86+
assertEquals("abc", reader.readLine());
87+
assertEquals("def", reader.readLine());
88+
assertEquals("ghi", reader.readLine());
89+
assertEquals("jkl", reader.readLine());
90+
}
91+
}
92+
5493
@Test
5594
public void testInput() throws IOException, InterruptedException {
5695
PipedInputStream in = new PipedInputStream();

‎terminal/src/main/java/org/jline/terminal/Attributes.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ public enum InputFlag {
5656
IXOFF, /* enable input flow control */
5757
IXANY, /* any char will restart after stop */
5858
IMAXBEL, /* ring bell on input queue full */
59-
IUTF8 /* maintain state for UTF-8 VERASE */
59+
IUTF8, /* maintain state for UTF-8 VERASE */
60+
61+
INORMEOL /* normalize end-of-line */
6062
}
6163

6264
/*

‎terminal/src/main/java/org/jline/terminal/impl/AbstractPty.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package org.jline.terminal.impl;
1010

1111
import java.io.FileDescriptor;
12+
import java.io.FilterInputStream;
1213
import java.io.IOError;
1314
import java.io.IOException;
1415
import java.io.InputStream;
@@ -34,6 +35,7 @@ public abstract class AbstractPty implements Pty {
3435
protected final TerminalProvider provider;
3536
protected final SystemStream systemStream;
3637
private Attributes current;
38+
private boolean skipNextLf;
3739

3840
public AbstractPty(TerminalProvider provider, SystemStream systemStream) {
3941
this.provider = provider;
@@ -49,10 +51,32 @@ public void setAttr(Attributes attr) throws IOException {
4951
@Override
5052
public InputStream getSlaveInput() throws IOException {
5153
InputStream si = doGetSlaveInput();
54+
InputStream nsi = new FilterInputStream(si) {
55+
@Override
56+
public int read() throws IOException {
57+
for (; ; ) {
58+
int c = super.read();
59+
if (current.getInputFlag(Attributes.InputFlag.INORMEOL)) {
60+
if (c == '\r') {
61+
skipNextLf = true;
62+
c = '\n';
63+
} else if (c == '\n') {
64+
if (skipNextLf) {
65+
skipNextLf = false;
66+
continue;
67+
}
68+
} else {
69+
skipNextLf = false;
70+
}
71+
}
72+
return c;
73+
}
74+
}
75+
};
5276
if (Boolean.parseBoolean(System.getProperty(PROP_NON_BLOCKING_READS, "true"))) {
53-
return new PtyInputStream(si);
77+
return new PtyInputStream(nsi);
5478
} else {
55-
return si;
79+
return nsi;
5680
}
5781
}
5882

‎terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public abstract class AbstractWindowsTerminal<Console> extends AbstractTerminal
8585
protected MouseTracking tracking = MouseTracking.Off;
8686
protected boolean focusTracking = false;
8787
private volatile boolean closing;
88+
protected boolean skipNextLf;
8889

8990
@SuppressWarnings("this-escape")
9091
public AbstractWindowsTerminal(
@@ -496,7 +497,19 @@ public void processInputChar(char c) throws IOException {
496497
raise(Signal.INFO);
497498
}
498499
}
499-
if (c == '\r') {
500+
if (attributes.getInputFlag(Attributes.InputFlag.INORMEOL)) {
501+
if (c == '\r') {
502+
skipNextLf = true;
503+
c = '\n';
504+
} else if (c == '\n') {
505+
if (skipNextLf) {
506+
skipNextLf = false;
507+
return;
508+
}
509+
} else {
510+
skipNextLf = false;
511+
}
512+
} else if (c == '\r') {
500513
if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) {
501514
return;
502515
}

‎terminal/src/main/java/org/jline/terminal/impl/DumbTerminal.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class DumbTerminal extends AbstractTerminal {
3434
private final PrintWriter writer;
3535
private final Attributes attributes;
3636
private final Size size;
37+
private boolean skipNextLf;
3738

3839
public DumbTerminal(InputStream in, OutputStream out) throws IOException {
3940
this(TYPE_DUMB, TYPE_DUMB, in, out, null);
@@ -79,7 +80,19 @@ public int read(long timeout, boolean isPeek) throws IOException {
7980
continue;
8081
}
8182
}
82-
if (c == '\r') {
83+
if (attributes.getInputFlag(Attributes.InputFlag.INORMEOL)) {
84+
if (c == '\r') {
85+
skipNextLf = true;
86+
c = '\n';
87+
} else if (c == '\n') {
88+
if (skipNextLf) {
89+
skipNextLf = false;
90+
continue;
91+
}
92+
} else {
93+
skipNextLf = false;
94+
}
95+
} else if (c == '\r') {
8396
if (attributes.getInputFlag(Attributes.InputFlag.IGNCR)) {
8497
continue;
8598
}

‎terminal/src/main/java/org/jline/terminal/impl/LineDisciplineTerminal.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public class LineDisciplineTerminal extends AbstractTerminal {
7575

7676
protected final Size size;
7777

78+
protected boolean skipNextLf;
79+
7880
public LineDisciplineTerminal(String name, String type, OutputStream masterOutput, Charset encoding)
7981
throws IOException {
8082
this(name, type, masterOutput, encoding, SignalHandler.SIG_DFL);
@@ -253,7 +255,19 @@ protected boolean doProcessInputByte(int c) throws IOException {
253255
raise(Signal.INFO);
254256
}
255257
}
256-
if (c == '\r') {
258+
if (attributes.getInputFlag(InputFlag.INORMEOL)) {
259+
if (c == '\r') {
260+
skipNextLf = true;
261+
c = '\n';
262+
} else if (c == '\n') {
263+
if (skipNextLf) {
264+
skipNextLf = false;
265+
return false;
266+
}
267+
} else {
268+
skipNextLf = false;
269+
}
270+
} else if (c == '\r') {
257271
if (attributes.getInputFlag(InputFlag.IGNCR)) {
258272
return false;
259273
}

‎terminal/src/main/java/org/jline/terminal/impl/exec/ExecPty.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ protected void doSetAttr(Attributes attr) throws IOException {
120120
protected List<String> getFlagsToSet(Attributes attr, Attributes current) {
121121
List<String> commands = new ArrayList<>();
122122
for (InputFlag flag : InputFlag.values()) {
123-
if (attr.getInputFlag(flag) != current.getInputFlag(flag)) {
123+
if (attr.getInputFlag(flag) != current.getInputFlag(flag) && flag != InputFlag.INORMEOL) {
124124
commands.add((attr.getInputFlag(flag) ? flag.name() : "-" + flag.name()).toLowerCase());
125125
}
126126
}

0 commit comments

Comments
 (0)
Please sign in to comment.