Skip to content

Commit c5b716c

Browse files
committed
arduino#1991: add support for 'clear screen' and 'move to home'
1 parent eb1f247 commit c5b716c

File tree

3 files changed

+245
-51
lines changed

3 files changed

+245
-51
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,81 @@
1-
import { Line, SerialMonitorOutput } from './serial-monitor-send-output';
1+
import {Line, SerialMonitorOutput} from './serial-monitor-send-output';
2+
3+
function writeOverLine(line: Line, insert: string, cursorPosition: number): [number, number] {
4+
var lenBefore = line.message.length;
5+
line.message = line.message.substring(0, cursorPosition) + insert + line.message.substring(cursorPosition + insert.length)
6+
cursorPosition = cursorPosition + insert.length;
7+
line.lineLen = line.message.length;
8+
return [line.lineLen - lenBefore, cursorPosition];
9+
}
10+
11+
const escapeSequenceGoHome = '\x1B[H';
12+
const escapeSequenceClearScreen = '\x1B[2J';
213

314
export function messagesToLines(
415
messages: string[],
516
prevLines: Line[] = [],
617
charCount = 0,
7-
separator = '\n'
8-
): [Line[], number] {
9-
const linesToAdd: Line[] = prevLines.length
10-
? [prevLines[prevLines.length - 1]]
11-
: [{ message: '', lineLen: 0 }];
12-
if (!(Symbol.iterator in Object(messages))) return [prevLines, charCount];
18+
currentLineIndex: number | null,
19+
currentCursorPosition: number,
20+
separator = '\n',
21+
): [Line[], number, number | null, number, string | null] {
22+
if (!prevLines.length) {
23+
prevLines = [{message: '', lineLen: 0, timestamp: new Date()}];
24+
}
25+
26+
currentLineIndex = currentLineIndex || 0;
1327

14-
for (const message of messages) {
15-
const messageLen = message.length;
16-
charCount += messageLen;
17-
const lastLine = linesToAdd[linesToAdd.length - 1];
28+
let allMessages = messages.join('');
29+
let overflow = null;
1830

19-
// if the previous messages ends with "separator" add a new line
20-
if (lastLine.message.charAt(lastLine.message.length - 1) === separator) {
21-
linesToAdd.push({
22-
message,
23-
timestamp: new Date(),
24-
lineLen: messageLen,
25-
});
26-
} else {
27-
// concatenate to the last line
28-
linesToAdd[linesToAdd.length - 1].message += message;
29-
linesToAdd[linesToAdd.length - 1].lineLen += messageLen;
30-
if (!linesToAdd[linesToAdd.length - 1].timestamp) {
31-
linesToAdd[linesToAdd.length - 1].timestamp = new Date();
31+
if (allMessages.indexOf(escapeSequenceGoHome) >= 0) {
32+
const before = allMessages.substring(0, allMessages.indexOf(escapeSequenceGoHome));
33+
const after = allMessages.substring(allMessages.indexOf(escapeSequenceGoHome) + escapeSequenceGoHome.length);
34+
const [_lines, _charCount] = messagesToLines([before], prevLines, charCount, currentLineIndex, currentCursorPosition, separator);
35+
return messagesToLines([after], _lines, _charCount, 0, 0, separator);
36+
} else if (allMessages.indexOf(escapeSequenceClearScreen) >= 0) {
37+
const after = allMessages.substring(allMessages.lastIndexOf(escapeSequenceClearScreen) + escapeSequenceClearScreen.length);
38+
return messagesToLines([after], [], 0, 0, 0, separator);
39+
} else if (allMessages.lastIndexOf('\x1B') >= 0) {
40+
overflow = allMessages.substring(allMessages.lastIndexOf('\x1B'));
41+
const result = messagesToLines([allMessages.substring(0, allMessages.lastIndexOf('\x1B'))], prevLines, charCount, currentLineIndex, currentCursorPosition, separator);
42+
result[4] = overflow;
43+
return result;
44+
}
45+
46+
const chunks = allMessages.split(separator);
47+
for (let i = 0; i < chunks.length; i++) {
48+
const chunk = chunks[i];
49+
if (chunk !== '') {
50+
if (prevLines[currentLineIndex].message[currentCursorPosition - 1] === '\n') {
51+
currentLineIndex++;
52+
currentCursorPosition = 0;
53+
}
54+
if (currentLineIndex > prevLines.length - 1) {
55+
prevLines.push({message: '', lineLen: 0, timestamp: new Date()});
3256
}
57+
let [_addedCharacters, _currentCursorPosition] = writeOverLine(prevLines[currentLineIndex], chunk, currentCursorPosition)
58+
charCount += _addedCharacters;
59+
currentCursorPosition = _currentCursorPosition;
60+
}
61+
62+
if (i < chunks.length - 1) {
63+
let [_addedCharacters, _currentCursorPosition] = writeOverLine(prevLines[currentLineIndex], separator, currentCursorPosition)
64+
charCount += _addedCharacters;
65+
currentCursorPosition = _currentCursorPosition;
3366
}
3467
}
3568

36-
prevLines.splice(prevLines.length - 1, 1, ...linesToAdd);
37-
return [prevLines, charCount];
69+
return [prevLines, charCount, currentLineIndex, currentCursorPosition, overflow]
3870
}
3971

4072
export function truncateLines(
4173
lines: Line[],
4274
charCount: number,
75+
currentLineIndex: number | null,
76+
currentCursorPosition: number,
4377
maxCharacters: number = SerialMonitorOutput.MAX_CHARACTERS
44-
): [Line[], number] {
78+
): [Line[], number, number | null, number] {
4579
let charsToDelete = charCount - maxCharacters;
4680
let lineIndex = 0;
4781
while (charsToDelete > 0 || lineIndex > 0) {
@@ -65,5 +99,5 @@ export function truncateLines(
6599
charsToDelete -= deletedCharsCount;
66100
lines[0].message = newFirstLine;
67101
}
68-
return [lines, charCount];
102+
return [lines, charCount, currentLineIndex, currentCursorPosition];
69103
}

Diff for: arduino-ide-extension/src/browser/serial/monitor/serial-monitor-send-output.tsx

+30-14
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export class SerialMonitorOutput extends React.Component<
2626
lines: [],
2727
timestamp: this.props.monitorModel.timestamp,
2828
charCount: 0,
29+
lineIndex: null,
30+
cursorPosition: 0,
31+
overflow: null
2932
};
3033
}
3134

@@ -57,22 +60,32 @@ export class SerialMonitorOutput extends React.Component<
5760
this.scrollToBottom();
5861
this.toDisposeBeforeUnmount.pushAll([
5962
this.props.monitorManagerProxy.onMessagesReceived(({ messages }) => {
60-
const [newLines, totalCharCount] = messagesToLines(
61-
messages,
62-
this.state.lines,
63-
this.state.charCount
64-
);
65-
const [lines, charCount] = truncateLines(newLines, totalCharCount);
66-
this.setState(
67-
{
68-
lines,
69-
charCount,
70-
},
71-
() => this.scrollToBottom()
72-
);
63+
if(Symbol.iterator in Object(messages)) {
64+
if (this.state.overflow) {
65+
messages[0] = this.state.overflow + messages[0];
66+
}
67+
const [newLines, totalCharCount, cLineIndex, cCursorPosition, overflow] = messagesToLines(
68+
messages,
69+
this.state.lines,
70+
this.state.charCount,
71+
this.state.lineIndex,
72+
this.state.cursorPosition,
73+
);
74+
const [lines, charCount, lineIndex, cursorPosition] = truncateLines(newLines, totalCharCount, cLineIndex, cCursorPosition);
75+
this.setState(
76+
{
77+
lines,
78+
charCount,
79+
lineIndex,
80+
cursorPosition,
81+
overflow
82+
},
83+
() => this.scrollToBottom()
84+
);
85+
}
7386
}),
7487
this.props.clearConsoleEvent(() =>
75-
this.setState({ lines: [], charCount: 0 })
88+
this.setState({ lines: [], charCount: 0, lineIndex: null, cursorPosition: 0, overflow: null })
7689
),
7790
this.props.monitorModel.onChange(({ property }) => {
7891
if (property === 'timestamp') {
@@ -137,6 +150,9 @@ export namespace SerialMonitorOutput {
137150
lines: Line[];
138151
timestamp: boolean;
139152
charCount: number;
153+
lineIndex: number | null;
154+
cursorPosition: number;
155+
overflow: string | null;
140156
}
141157

142158
export interface SelectOption<T> {

0 commit comments

Comments
 (0)