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 { SerialConfig } from '../../../common/protocol/serial-service'; import { ArduinoSelect } from '../../widgets/arduino-select'; import { SerialModel } from '../serial-model'; import { SerialConnectionManager } from '../serial-connection-manager'; import { SerialMonitorSendInput } from './serial-monitor-send-input'; import { SerialMonitorOutput } from './serial-monitor-send-output'; import { BoardsServiceProvider } from '../../boards/boards-service-provider'; import { nls } from '@theia/core/lib/common'; @injectable() export class MonitorWidget extends ReactWidget { static readonly LABEL = nls.localize( 'arduino/common/serialMonitor', 'Serial Monitor' ); static readonly ID = 'serial-monitor'; @inject(SerialModel) protected readonly serialModel: SerialModel; @inject(SerialConnectionManager) protected readonly serialConnection: SerialConnectionManager; @inject(BoardsServiceProvider) protected readonly boardsServiceProvider: BoardsServiceProvider; 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 = MonitorWidget.LABEL; this.title.iconClass = 'monitor-tab-icon'; this.title.closable = true; this.scrollOptions = undefined; this.toDispose.push(this.clearOutputEmitter); this.toDispose.push( Disposable.create(() => this.serialConnection.closeWStoBE()) ); } @postConstruct() protected init(): void { this.update(); this.toDispose.push( this.serialConnection.onConnectionChanged(() => this.clearConsole()) ); this.toDispose.push(this.serialModel.onChange(() => this.update())); } clearConsole(): void { this.clearOutputEmitter.fire(undefined); this.update(); } dispose(): void { super.dispose(); } protected onAfterAttach(msg: Message): void { super.onAfterAttach(msg); this.serialConnection.openWSToBE(); } 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: nls.localize('arduino/serial/noLineEndings', 'No Line Ending'), value: '', }, { label: nls.localize('arduino/serial/newLine', 'New Line'), value: '\n', }, { label: nls.localize('arduino/serial/carriageReturn', 'Carriage Return'), value: '\r', }, { label: nls.localize( 'arduino/serial/newLineCarriageReturn', '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.serialModel.lineEnding) || lineEndings[1]; // Defaults to `\n`. const baudRate = baudRates.find((item) => item.value === this.serialModel.baudRate) || baudRates[4]; // Defaults to `9600`. return (
); } protected readonly onSend = (value: string) => this.doSend(value); protected async doSend(value: string): Promise { this.serialConnection.send(value); } protected readonly onChangeLineEnding = ( option: SerialMonitorOutput.SelectOption ) => { this.serialModel.lineEnding = option.value; }; protected readonly onChangeBaudRate = ( option: SerialMonitorOutput.SelectOption ) => { this.serialModel.baudRate = option.value; }; }