Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a buffer for unfinished lines during progress bar printing. the lines will be completed by the next print, or way at the end when all progress bars are done. #1951

Merged
merged 8 commits into from
May 24, 2024
96 changes: 45 additions & 51 deletions src/org/rascalmpl/repl/TerminalProgressBarMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

Expand Down Expand Up @@ -42,7 +43,7 @@
* Since there are generally very few threads a simple list beats a hash-map in terms
* of memory allocation and possibly also lookup efficiency.
*/
private List<UnfinishedLine> unfinishedLines = new ArrayList<>(3);

Check warning on line 46 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L46

Added line #L46 was not covered by tests

/**
* This writer is there to help with the encoding to what the terminal needs. It writes directly to the
Expand Down Expand Up @@ -117,93 +118,86 @@
}
}

private class UnfinishedLine {
private static class UnfinishedLine {
final long threadId;
int curCapacity = 512;
byte[] buffer = new byte[curCapacity];
int curEnd = 0;
int lastNewLine = 0;
private int curCapacity = 512;
private byte[] buffer = new byte[curCapacity];
private int curEnd = 0;

Check warning on line 125 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L123-L125

Added lines #L123 - L125 were not covered by tests

public UnfinishedLine() {
this.threadId = Thread.currentThread().getId();
}

Check warning on line 129 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L127-L129

Added lines #L127 - L129 were not covered by tests

/**
* Adding input combines previously unfinished sentences with possible
* new sentences. A number of cases come together here that otherwise
* should be diligently separated.
*
* - An unfinished line can already exist or not
* - The new input can contain newlines or not
* - The new input can end with a newline character or not
*
* By concatenating the previous unfinished line with the new input
* all we have to do now is figure out where the last newline character is.
* new (unfinished) sentences.
*
* The resulting buffer nevers contain any newline character.
*/
private void add(byte[] newInput, int offset, int len) {
private void store(byte[] newInput, int offset, int len) {
// first ensure capacity of the array
if (curEnd + len >= curCapacity) {
var oldCapacity = curCapacity;
curCapacity *= 2; // this should not happen to often. we're talking a few lines of text.
byte[] tmp = new byte[curCapacity];
System.arraycopy(buffer, 0, tmp, 0, oldCapacity);
buffer = tmp;
curCapacity *= 2;
buffer = Arrays.copyOf(buffer, curCapacity);

Check warning on line 141 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L140-L141

Added lines #L140 - L141 were not covered by tests
}

System.arraycopy(newInput, offset, buffer, curEnd, len);
curEnd += len;
lastNewLine = startOfLastLine();
}

Check warning on line 146 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L144-L146

Added lines #L144 - L146 were not covered by tests

public void write(byte[] n, OutputStream out) throws IOException {
add(n, 0, n.length);
flushToLastLine(out);
write(n, 0, n.length, out);
}

Check warning on line 150 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L149-L150

Added lines #L149 - L150 were not covered by tests

/**
* Main workhorse looks for newline characters in the new input.
* - if there are newlines, than whatever is in the buffer can be flushed.
* - all the characters up to the last new new line are flushed immediately.
* - all the new characters after the last newline are buffered.
*/
public void write(byte[] n, int offset, int len, OutputStream out) throws IOException {
add(n, offset, len);
flushToLastLine(out);
}

private void flushToLastLine(OutputStream out) throws IOException {
if (lastNewLine != -1) {
// write everything out (except the last unfinished line), but including the last newline
out.write(buffer, 0, lastNewLine + 1);

if (lastNewLine + 1 == curEnd) {
// nothing left
curEnd = 0;
lastNewLine = -1;
}
else {
// copy the last line that does not end with a newline
curEnd -= lastNewLine;
System.arraycopy(buffer, lastNewLine + 1, buffer, 0, curEnd);
lastNewLine = -1; // no newline anymore
}
}
int lastNL = startOfLastLine(n, offset, len);

Check warning on line 159 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L159

Added line #L159 was not covered by tests

// otherwise we wait until the next input comes to be able to complete a line.
if (lastNL == -1) {
store(n, offset, len);

Check warning on line 162 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L162

Added line #L162 was not covered by tests
}
else {
flush(out);
out.write(n, offset, lastNL + 1);
store(n, offset, lastNL - offset);

Check warning on line 167 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L165-L167

Added lines #L165 - L167 were not covered by tests
DavyLandman marked this conversation as resolved.
Show resolved Hide resolved
}
}

Check warning on line 169 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L169

Added line #L169 was not covered by tests

public void writeLeftOvers(OutputStream out) throws IOException {
/**
* This empties the current buffer onto the stream,
* and resets the cursor.
*/
private void flush(OutputStream out) throws IOException {
if (curEnd != 0) {
out.write(buffer, 0, curEnd);
out.write('\n');
curEnd = 0;

Check warning on line 178 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L177-L178

Added lines #L177 - L178 were not covered by tests
lastNewLine = -1;
}
}

Check warning on line 180 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L180

Added line #L180 was not covered by tests

/**
* Prints whatever is the last line in the buffer,
* and adds a newline.
*/
public void flushLastLine(OutputStream out) throws IOException {
if (curEnd != 0) {
flush(out);
out.write('\n');

Check warning on line 189 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L188-L189

Added lines #L188 - L189 were not covered by tests
}
}

Check warning on line 191 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L191

Added line #L191 was not covered by tests

private int startOfLastLine() {
for (int i = curEnd - 1; i >= 0; i--) {
private int startOfLastLine(byte[] buffer, int offset, int len) {
for (int i = offset + len - 1; i >= 0; i--) {
DavyLandman marked this conversation as resolved.
Show resolved Hide resolved
if (buffer[i] == '\n') {
return i;

Check warning on line 196 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L196

Added line #L196 was not covered by tests
}
}

return -1;

Check warning on line 200 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L200

Added line #L200 was not covered by tests
}
}

Expand Down Expand Up @@ -332,7 +326,7 @@
+ " "
;

writer.println(line); // note this puts us one line down

Check warning on line 329 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L329

Added line #L329 was not covered by tests
}

private String threadLabel() {
Expand Down Expand Up @@ -495,18 +489,18 @@
}

private UnfinishedLine findUnfinishedLine() {
UnfinishedLine before = unfinishedLines.stream()

Check warning on line 492 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L492

Added line #L492 was not covered by tests
.filter(l -> l.threadId == Thread.currentThread().getId())
.findAny()
.orElse(null);

Check warning on line 495 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L494-L495

Added lines #L494 - L495 were not covered by tests

if (before == null) {
UnfinishedLine l = new UnfinishedLine();
jurgenvinju marked this conversation as resolved.
Show resolved Hide resolved
unfinishedLines.add(l);
before = l;

Check warning on line 500 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L498-L500

Added lines #L498 - L500 were not covered by tests
}

return before;

Check warning on line 503 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L503

Added line #L503 was not covered by tests
}

@Override
Expand Down Expand Up @@ -606,7 +600,7 @@
if (!bars.isEmpty()) {
eraseBars();

findUnfinishedLine().write(b, out);

Check warning on line 603 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L603

Added line #L603 was not covered by tests

printBars();
}
Expand All @@ -624,7 +618,7 @@
public synchronized void write(byte[] b, int off, int len) throws IOException {
if (!bars.isEmpty()) {
eraseBars();
findUnfinishedLine().write(b, off, len, out);

Check warning on line 621 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L621

Added line #L621 was not covered by tests
printBars();
}
else {
Expand All @@ -641,7 +635,7 @@
public synchronized void write(int b) throws IOException {
if (!bars.isEmpty()) {
eraseBars();
findUnfinishedLine().write(new byte[] { (byte) b }, out);

Check warning on line 638 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L638

Added line #L638 was not covered by tests
printBars();
}
else {
Expand All @@ -662,12 +656,12 @@
bars.clear();
for (UnfinishedLine l : unfinishedLines) {
try {
l.writeLeftOvers(out);
l.flushLastLine(out);

Check warning on line 659 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L659

Added line #L659 was not covered by tests
}
catch (IOException e) {

Check warning on line 661 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L661

Added line #L661 was not covered by tests
// might happen if the terminal crashes before we stop running
}
}

Check warning on line 664 in src/org/rascalmpl/repl/TerminalProgressBarMonitor.java

View check run for this annotation

Codecov / codecov/patch

src/org/rascalmpl/repl/TerminalProgressBarMonitor.java#L663-L664

Added lines #L663 - L664 were not covered by tests
writer.write(ANSI.showCursor());
writer.flush();
}
Expand Down
Loading