mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-14 16:16:32 +00:00
148 lines
3.8 KiB
TypeScript
148 lines
3.8 KiB
TypeScript
import * as React from 'react';
|
|
import { Event } from '@theia/core/lib/common/event';
|
|
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
import { areEqual, FixedSizeList as List } from 'react-window';
|
|
import { SerialModel } from '../serial-model';
|
|
import { SerialConnectionManager } from '../serial-connection-manager';
|
|
import dateFormat = require('dateformat');
|
|
import { messagesToLines, truncateLines } from './monitor-utils';
|
|
|
|
export type Line = { message: string; timestamp?: Date; lineLen: number };
|
|
|
|
export class SerialMonitorOutput extends React.Component<
|
|
SerialMonitorOutput.Props,
|
|
SerialMonitorOutput.State
|
|
> {
|
|
/**
|
|
* Do not touch it. It is used to be able to "follow" the serial monitor log.
|
|
*/
|
|
protected toDisposeBeforeUnmount = new DisposableCollection();
|
|
private listRef: React.RefObject<any>;
|
|
|
|
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
|
super(props);
|
|
this.listRef = React.createRef();
|
|
this.state = {
|
|
lines: [],
|
|
timestamp: this.props.serialModel.timestamp,
|
|
charCount: 0,
|
|
};
|
|
}
|
|
|
|
render(): React.ReactNode {
|
|
return (
|
|
<List
|
|
className="serial-monitor-messages"
|
|
height={this.props.height}
|
|
itemData={
|
|
{
|
|
lines: this.state.lines,
|
|
timestamp: this.state.timestamp,
|
|
} as any
|
|
}
|
|
itemCount={this.state.lines.length}
|
|
itemSize={18}
|
|
width={'100%'}
|
|
style={{ whiteSpace: 'nowrap' }}
|
|
ref={this.listRef}
|
|
>
|
|
{Row}
|
|
</List>
|
|
);
|
|
}
|
|
|
|
shouldComponentUpdate(): boolean {
|
|
return true;
|
|
}
|
|
|
|
componentDidMount(): void {
|
|
this.scrollToBottom();
|
|
this.toDisposeBeforeUnmount.pushAll([
|
|
this.props.serialConnection.onRead(({ messages }) => {
|
|
const [newLines, totalCharCount] = messagesToLines(
|
|
messages,
|
|
this.state.lines,
|
|
this.state.charCount
|
|
);
|
|
const [lines, charCount] = truncateLines(newLines, totalCharCount);
|
|
|
|
this.setState({
|
|
lines,
|
|
charCount,
|
|
});
|
|
this.scrollToBottom();
|
|
}),
|
|
this.props.clearConsoleEvent(() =>
|
|
this.setState({ lines: [], charCount: 0 })
|
|
),
|
|
this.props.serialModel.onChange(({ property }) => {
|
|
if (property === 'timestamp') {
|
|
const { timestamp } = this.props.serialModel;
|
|
this.setState({ timestamp });
|
|
}
|
|
if (property === 'autoscroll') {
|
|
this.scrollToBottom();
|
|
}
|
|
}),
|
|
]);
|
|
}
|
|
|
|
componentWillUnmount(): void {
|
|
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
|
this.toDisposeBeforeUnmount.dispose();
|
|
}
|
|
|
|
scrollToBottom = ((): void => {
|
|
if (this.listRef.current && this.props.serialModel.autoscroll) {
|
|
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
|
}
|
|
}).bind(this);
|
|
}
|
|
|
|
const _Row = ({
|
|
index,
|
|
style,
|
|
data,
|
|
}: {
|
|
index: number;
|
|
style: any;
|
|
data: { lines: Line[]; timestamp: boolean };
|
|
}) => {
|
|
const timestamp =
|
|
(data.timestamp &&
|
|
`${dateFormat(data.lines[index].timestamp, 'H:M:ss.l')} -> `) ||
|
|
'';
|
|
return (
|
|
(data.lines[index].lineLen && (
|
|
<div style={style}>
|
|
{timestamp}
|
|
{data.lines[index].message}
|
|
</div>
|
|
)) ||
|
|
null
|
|
);
|
|
};
|
|
const Row = React.memo(_Row, areEqual);
|
|
|
|
export namespace SerialMonitorOutput {
|
|
export interface Props {
|
|
readonly serialModel: SerialModel;
|
|
readonly serialConnection: SerialConnectionManager;
|
|
readonly clearConsoleEvent: Event<void>;
|
|
readonly height: number;
|
|
}
|
|
|
|
export interface State {
|
|
lines: Line[];
|
|
timestamp: boolean;
|
|
charCount: number;
|
|
}
|
|
|
|
export interface SelectOption<T> {
|
|
readonly label: string;
|
|
readonly value: T;
|
|
}
|
|
|
|
export const MAX_CHARACTERS = 1_000_000;
|
|
}
|