import * as React from 'react'; import { postConstruct, injectable, inject } from 'inversify'; import { OptionsType } from 'react-select/src/types'; import { Emitter } from '@theia/core/lib/common/event'; import { Disposable } from '@theia/core/lib/common/disposable'; import { ReactWidget, Message, Widget, MessageLoop, } from '@theia/core/lib/browser/widgets'; import { MonitorConfig } from '../../common/protocol/monitor-service'; import { ArduinoSelect } from '../widgets/arduino-select'; import { MonitorModel } from './monitor-model'; import { MonitorConnection } from './monitor-connection'; import { SerialMonitorSendInput } from './serial-monitor-send-input'; import { SerialMonitorOutput } from './serial-monitor-send-output'; @injectable() export class MonitorWidget extends ReactWidget { static readonly ID = 'serial-monitor'; @inject(MonitorModel) protected readonly monitorModel: MonitorModel; @inject(MonitorConnection) protected readonly monitorConnection: MonitorConnection; protected widgetHeight: number; /** * Do not touch or use it. It is for setting the focus on the `input` after the widget activation. */ protected focusNode: HTMLElement | undefined; /** * Guard against re-rendering the view after the close was requested. * See: https://github.com/eclipse-theia/theia/issues/6704 */ protected closing = false; protected readonly clearOutputEmitter = new Emitter(); constructor() { super(); this.id = MonitorWidget.ID; this.title.label = 'Serial Monitor'; this.title.iconClass = 'monitor-tab-icon'; this.title.closable = true; this.scrollOptions = undefined; this.toDispose.push(this.clearOutputEmitter); this.toDispose.push( Disposable.create(() => { this.monitorConnection.autoConnect = false; if (this.monitorConnection.connected) { this.monitorConnection.disconnect(); } }) ); } @postConstruct() protected init(): void { this.update(); this.toDispose.push( this.monitorConnection.onConnectionChanged(() => this.clearConsole()) ); } clearConsole(): void { this.clearOutputEmitter.fire(undefined); this.update(); } dispose(): void { super.dispose(); } protected onAfterAttach(msg: Message): void { super.onAfterAttach(msg); this.monitorConnection.autoConnect = true; } onCloseRequest(msg: Message): void { this.closing = true; super.onCloseRequest(msg); } protected onUpdateRequest(msg: Message): void { // TODO: `this.isAttached` // See: https://github.com/eclipse-theia/theia/issues/6704#issuecomment-562574713 if (!this.closing && this.isAttached) { super.onUpdateRequest(msg); } } protected onResize(msg: Widget.ResizeMessage): void { super.onResize(msg); this.widgetHeight = msg.height; this.update(); } protected onActivateRequest(msg: Message): void { super.onActivateRequest(msg); (this.focusNode || this.node).focus(); } protected onFocusResolved = (element: HTMLElement | undefined) => { if (this.closing || !this.isAttached) { return; } this.focusNode = element; requestAnimationFrame(() => MessageLoop.sendMessage(this, Widget.Msg.ActivateRequest) ); }; protected get lineEndings(): OptionsType< SerialMonitorOutput.SelectOption > { return [ { label: 'No Line Ending', value: '', }, { label: 'New Line', value: '\n', }, { label: 'Carriage Return', value: '\r', }, { label: 'Both NL & CR', value: '\r\n', }, ]; } protected get baudRates(): OptionsType< SerialMonitorOutput.SelectOption > { const baudRates: Array = [ 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, ]; return baudRates.map((baudRate) => ({ label: baudRate + ' baud', value: baudRate, })); } protected render(): React.ReactNode { const { baudRates, lineEndings } = this; const lineEnding = lineEndings.find((item) => item.value === this.monitorModel.lineEnding) || lineEndings[1]; // Defaults to `\n`. const baudRate = baudRates.find((item) => item.value === this.monitorModel.baudRate) || baudRates[4]; // Defaults to `9600`. return (
); } protected readonly onSend = (value: string) => this.doSend(value); protected async doSend(value: string): Promise { this.monitorConnection.send(value); } protected readonly onChangeLineEnding = ( option: SerialMonitorOutput.SelectOption ) => { this.monitorModel.lineEnding = option.value; }; protected readonly onChangeBaudRate = ( option: SerialMonitorOutput.SelectOption ) => { this.monitorModel.baudRate = option.value; }; }