Skip to content

Commit

Permalink
arduino#1991: add support for 'clear screen' and 'move to home'
Browse files Browse the repository at this point in the history
  • Loading branch information
wodzuu committed Apr 3, 2023
1 parent eb1f247 commit c5b716c
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 51 deletions.
90 changes: 62 additions & 28 deletions arduino-ide-extension/src/browser/serial/monitor/monitor-utils.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,81 @@
import { Line, SerialMonitorOutput } from './serial-monitor-send-output';
import {Line, SerialMonitorOutput} from './serial-monitor-send-output';

function writeOverLine(line: Line, insert: string, cursorPosition: number): [number, number] {
var lenBefore = line.message.length;
line.message = line.message.substring(0, cursorPosition) + insert + line.message.substring(cursorPosition + insert.length)
cursorPosition = cursorPosition + insert.length;
line.lineLen = line.message.length;
return [line.lineLen - lenBefore, cursorPosition];
}

const escapeSequenceGoHome = '\x1B[H';
const escapeSequenceClearScreen = '\x1B[2J';

export function messagesToLines(
messages: string[],
prevLines: Line[] = [],
charCount = 0,
separator = '\n'
): [Line[], number] {
const linesToAdd: Line[] = prevLines.length
? [prevLines[prevLines.length - 1]]
: [{ message: '', lineLen: 0 }];
if (!(Symbol.iterator in Object(messages))) return [prevLines, charCount];
currentLineIndex: number | null,
currentCursorPosition: number,
separator = '\n',
): [Line[], number, number | null, number, string | null] {
if (!prevLines.length) {
prevLines = [{message: '', lineLen: 0, timestamp: new Date()}];
}

currentLineIndex = currentLineIndex || 0;

for (const message of messages) {
const messageLen = message.length;
charCount += messageLen;
const lastLine = linesToAdd[linesToAdd.length - 1];
let allMessages = messages.join('');
let overflow = null;

// if the previous messages ends with "separator" add a new line
if (lastLine.message.charAt(lastLine.message.length - 1) === separator) {
linesToAdd.push({
message,
timestamp: new Date(),
lineLen: messageLen,
});
} else {
// concatenate to the last line
linesToAdd[linesToAdd.length - 1].message += message;
linesToAdd[linesToAdd.length - 1].lineLen += messageLen;
if (!linesToAdd[linesToAdd.length - 1].timestamp) {
linesToAdd[linesToAdd.length - 1].timestamp = new Date();
if (allMessages.indexOf(escapeSequenceGoHome) >= 0) {
const before = allMessages.substring(0, allMessages.indexOf(escapeSequenceGoHome));
const after = allMessages.substring(allMessages.indexOf(escapeSequenceGoHome) + escapeSequenceGoHome.length);
const [_lines, _charCount] = messagesToLines([before], prevLines, charCount, currentLineIndex, currentCursorPosition, separator);
return messagesToLines([after], _lines, _charCount, 0, 0, separator);
} else if (allMessages.indexOf(escapeSequenceClearScreen) >= 0) {
const after = allMessages.substring(allMessages.lastIndexOf(escapeSequenceClearScreen) + escapeSequenceClearScreen.length);
return messagesToLines([after], [], 0, 0, 0, separator);
} else if (allMessages.lastIndexOf('\x1B') >= 0) {
overflow = allMessages.substring(allMessages.lastIndexOf('\x1B'));
const result = messagesToLines([allMessages.substring(0, allMessages.lastIndexOf('\x1B'))], prevLines, charCount, currentLineIndex, currentCursorPosition, separator);
result[4] = overflow;
return result;
}

const chunks = allMessages.split(separator);
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
if (chunk !== '') {
if (prevLines[currentLineIndex].message[currentCursorPosition - 1] === '\n') {
currentLineIndex++;
currentCursorPosition = 0;
}
if (currentLineIndex > prevLines.length - 1) {
prevLines.push({message: '', lineLen: 0, timestamp: new Date()});
}
let [_addedCharacters, _currentCursorPosition] = writeOverLine(prevLines[currentLineIndex], chunk, currentCursorPosition)
charCount += _addedCharacters;
currentCursorPosition = _currentCursorPosition;
}

if (i < chunks.length - 1) {
let [_addedCharacters, _currentCursorPosition] = writeOverLine(prevLines[currentLineIndex], separator, currentCursorPosition)
charCount += _addedCharacters;
currentCursorPosition = _currentCursorPosition;
}
}

prevLines.splice(prevLines.length - 1, 1, ...linesToAdd);
return [prevLines, charCount];
return [prevLines, charCount, currentLineIndex, currentCursorPosition, overflow]
}

export function truncateLines(
lines: Line[],
charCount: number,
currentLineIndex: number | null,
currentCursorPosition: number,
maxCharacters: number = SerialMonitorOutput.MAX_CHARACTERS
): [Line[], number] {
): [Line[], number, number | null, number] {
let charsToDelete = charCount - maxCharacters;
let lineIndex = 0;
while (charsToDelete > 0 || lineIndex > 0) {
Expand All @@ -65,5 +99,5 @@ export function truncateLines(
charsToDelete -= deletedCharsCount;
lines[0].message = newFirstLine;
}
return [lines, charCount];
return [lines, charCount, currentLineIndex, currentCursorPosition];
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export class SerialMonitorOutput extends React.Component<
lines: [],
timestamp: this.props.monitorModel.timestamp,
charCount: 0,
lineIndex: null,
cursorPosition: 0,
overflow: null
};
}

Expand Down Expand Up @@ -57,22 +60,32 @@ export class SerialMonitorOutput extends React.Component<
this.scrollToBottom();
this.toDisposeBeforeUnmount.pushAll([
this.props.monitorManagerProxy.onMessagesReceived(({ messages }) => {
const [newLines, totalCharCount] = messagesToLines(
messages,
this.state.lines,
this.state.charCount
);
const [lines, charCount] = truncateLines(newLines, totalCharCount);
this.setState(
{
lines,
charCount,
},
() => this.scrollToBottom()
);
if(Symbol.iterator in Object(messages)) {
if (this.state.overflow) {
messages[0] = this.state.overflow + messages[0];
}
const [newLines, totalCharCount, cLineIndex, cCursorPosition, overflow] = messagesToLines(
messages,
this.state.lines,
this.state.charCount,
this.state.lineIndex,
this.state.cursorPosition,
);
const [lines, charCount, lineIndex, cursorPosition] = truncateLines(newLines, totalCharCount, cLineIndex, cCursorPosition);
this.setState(
{
lines,
charCount,
lineIndex,
cursorPosition,
overflow
},
() => this.scrollToBottom()
);
}
}),
this.props.clearConsoleEvent(() =>
this.setState({ lines: [], charCount: 0 })
this.setState({ lines: [], charCount: 0, lineIndex: null, cursorPosition: 0, overflow: null })
),
this.props.monitorModel.onChange(({ property }) => {
if (property === 'timestamp') {
Expand Down Expand Up @@ -137,6 +150,9 @@ export namespace SerialMonitorOutput {
lines: Line[];
timestamp: boolean;
charCount: number;
lineIndex: number | null;
cursorPosition: number;
overflow: string | null;
}

export interface SelectOption<T> {
Expand Down
Loading

0 comments on commit c5b716c

Please sign in to comment.