Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
#1991: add support for 'clear screen' and 'move to home'
  • Loading branch information
wodzuu committed Apr 3, 2023
commit c5b716c7da6e702ed3c5224906d087f09d258da3
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