mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-11-17 22:29:27 +00:00
* Add a copy output button to serial monitor
If the arduino collects some data that you want to store on your
computer, a rather simple way is to write it to the serial monitor and
copy it to the clipboard. This commit introduces a button that copies
the whole content of the serial monitor to the clipboard to make this
rather simple. It is a new component added to the menu, and does not
change the behaviour of other compontents.
* Test merging lines to str in serial monitor utils
Adds a test for merging one or more lines to a single string. It is
supposed to just concatenate the content of the lines, without doing
anything else. This method is used when copying the serial monitor content to
the clipboard.
* Add copy output translation key
This serves as an addition to the previous commits. It is the result of
running `yarn i18n:generate` on the state after adding the copy output
button to the serial monitor (see 2df3f465). I hope that this will
resolve the current Github action failure.
* Improve readability for serial monitor utils
Replace return statement in inline method by direct statement, some
minor formatting changes. Does not affect the functionality.
* Rename linesToMergedStr in monitor-utils
Renames the method linesToMergedStr to joinLines in the serial monitor
utils. This brings the name more in line with truncateLines. No
functionality changes.
* Move label and icon registration for copy serial
Moves the registration of the label and icon for the copy output
button of the serial monitor to the toolbar item registration. Before,
it happened at the command registration, but is not necessary at this
level, as the icon and label are meant for the toolbar button only.
* Do not update widget when copying output
No longer updates the serial monitor output after its content is copied.
Copying the content does not change anything for the view, so there is
no need to update.
155 lines
4.2 KiB
TypeScript
155 lines
4.2 KiB
TypeScript
import React from '@theia/core/shared/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 dateFormat from 'dateformat';
|
|
import { messagesToLines, truncateLines, joinLines } from './monitor-utils';
|
|
import { MonitorManagerProxyClient } from '../../../common/protocol';
|
|
import { MonitorModel } from '../../monitor-model';
|
|
import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
|
|
|
|
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<List>;
|
|
|
|
constructor(props: Readonly<SerialMonitorOutput.Props>) {
|
|
super(props);
|
|
this.listRef = React.createRef();
|
|
this.state = {
|
|
lines: [],
|
|
timestamp: this.props.monitorModel.timestamp,
|
|
charCount: 0,
|
|
};
|
|
}
|
|
|
|
override render(): React.ReactNode {
|
|
return (
|
|
<List
|
|
className="serial-monitor-messages"
|
|
height={this.props.height}
|
|
itemData={{
|
|
lines: this.state.lines,
|
|
timestamp: this.state.timestamp,
|
|
}}
|
|
itemCount={this.state.lines.length}
|
|
itemSize={18}
|
|
width={'100%'}
|
|
style={{ whiteSpace: 'nowrap' }}
|
|
ref={this.listRef}
|
|
>
|
|
{Row}
|
|
</List>
|
|
);
|
|
}
|
|
|
|
override shouldComponentUpdate(): boolean {
|
|
return true;
|
|
}
|
|
|
|
override componentDidMount(): void {
|
|
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()
|
|
);
|
|
}),
|
|
this.props.clearConsoleEvent(() =>
|
|
this.setState({ lines: [], charCount: 0 })
|
|
),
|
|
this.props.copyOutputEvent(() =>
|
|
this.props.clipboardService.writeText(joinLines(this.state.lines))
|
|
),
|
|
this.props.monitorModel.onChange(({ property }) => {
|
|
if (property === 'timestamp') {
|
|
const { timestamp } = this.props.monitorModel;
|
|
this.setState({ timestamp });
|
|
}
|
|
if (property === 'autoscroll') {
|
|
this.scrollToBottom();
|
|
}
|
|
}),
|
|
]);
|
|
}
|
|
|
|
override componentWillUnmount(): void {
|
|
// TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout?
|
|
this.toDisposeBeforeUnmount.dispose();
|
|
}
|
|
|
|
private readonly scrollToBottom = () => {
|
|
if (this.listRef.current && this.props.monitorModel.autoscroll) {
|
|
this.listRef.current.scrollToItem(this.state.lines.length, 'end');
|
|
}
|
|
};
|
|
}
|
|
|
|
const _Row = ({
|
|
index,
|
|
style,
|
|
data,
|
|
}: {
|
|
index: number;
|
|
style: any;
|
|
data: { lines: Line[]; timestamp: boolean };
|
|
}) => {
|
|
const timestamp =
|
|
(data.timestamp &&
|
|
`${dateFormat(data.lines[index].timestamp, 'HH:MM:ss.l')} -> `) ||
|
|
'';
|
|
return (
|
|
(data.lines[index].lineLen && (
|
|
<div style={style}>
|
|
<pre>
|
|
{timestamp}
|
|
{data.lines[index].message}
|
|
</pre>
|
|
</div>
|
|
)) ||
|
|
null
|
|
);
|
|
};
|
|
const Row = React.memo(_Row, areEqual);
|
|
|
|
export namespace SerialMonitorOutput {
|
|
export interface Props {
|
|
readonly monitorModel: MonitorModel;
|
|
readonly monitorManagerProxy: MonitorManagerProxyClient;
|
|
readonly clearConsoleEvent: Event<void>;
|
|
readonly copyOutputEvent: Event<void>;
|
|
readonly clipboardService: ClipboardService;
|
|
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;
|
|
}
|