From 9597c8e7bfad3b1fbbd18c591c8cbd7bb81e879a Mon Sep 17 00:00:00 2001 From: Radu Date: Tue, 20 Dec 2022 09:53:16 +0200 Subject: [PATCH 1/6] First commit --- .../browser/arduino-ide-frontend-module.ts | 13 ++ .../browser/serial/decode/decode-output.tsx | 122 ++++++++++++++++ .../serial/decode/decode-send-input.tsx | 135 ++++++++++++++++++ .../src/browser/serial/decode/decode-view.tsx | 110 ++++++++++++++ .../browser/serial/decode/decode-widget.tsx | 135 ++++++++++++++++++ .../browser/serial/monitor/monitor-widget.tsx | 8 ++ 6 files changed, 523 insertions(+) create mode 100644 arduino-ide-extension/src/browser/serial/decode/decode-output.tsx create mode 100644 arduino-ide-extension/src/browser/serial/decode/decode-send-input.tsx create mode 100644 arduino-ide-extension/src/browser/serial/decode/decode-view.tsx create mode 100644 arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index dfeffb0cb..61e3306d5 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -75,6 +75,7 @@ import { ConfigServicePath, } from '../common/protocol/config-service'; import { MonitorWidget } from './serial/monitor/monitor-widget'; +import { DecodeWidget } from './serial/decode/decode-widget'; import { MonitorViewContribution } from './serial/monitor/monitor-view-contribution'; import { TabBarDecoratorService as TheiaTabBarDecoratorService } from '@theia/core/lib/browser/shell/tab-bar-decorator'; import { TabBarDecoratorService } from './theia/core/tab-bar-decorator'; @@ -337,6 +338,7 @@ import { InterfaceScale } from './contributions/interface-scale'; import { OpenHandler } from '@theia/core/lib/browser/opener-service'; import { NewCloudSketch } from './contributions/new-cloud-sketch'; import { SketchbookCompositeWidget } from './widgets/sketchbook/sketchbook-composite-widget'; +import { DecodeViewContribution } from './serial/decode/decode-view'; const registerArduinoThemes = () => { const themes: MonacoThemeJson[] = [ @@ -491,6 +493,17 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); bind(CoreErrorHandler).toSelf().inSingletonScope(); + // Decode box + bind(DecodeWidget).toSelf(); + bindViewContribution(bind, DecodeViewContribution); + bind(TabBarToolbarContribution).toService(DecodeViewContribution); + bind(WidgetFactory).toDynamicValue((context) => ({ + id: DecodeWidget.ID, + createWidget: () => { + return new DecodeWidget(); + }, + })); + // Serial monitor bind(MonitorWidget).toSelf(); bind(FrontendApplicationContribution).toService(MonitorModel); diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx new file mode 100644 index 000000000..1ef41bcaa --- /dev/null +++ b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx @@ -0,0 +1,122 @@ +import * as React from '@theia/core/shared/react'; +import { Event } from '@theia/core/lib/common/event'; +import { DisposableCollection } from '@theia/core/lib/common/disposable'; +// import { areEqual } from 'react-window'; + +export type Line = { message: string; lineLen: number }; + +export class DecodeOutput extends React.Component< + DecodeOutput.Props, + DecodeOutput.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; + + constructor(props: Readonly) { + super(props); + this.listRef = React.createRef(); + this.state = { + lines: [], + charCount: 0, + text: '', + }; + } + + decodeText = (value: string) => { + console.log("decodeText works!", value); + this.setState({text: value}); + } + + override render(): React.ReactNode { + return ( + // + // {Row} + // +
{this.state.text}
+ ); + } + + override shouldComponentUpdate(): boolean { + return true; + } + + override componentDidMount(): void { + this.scrollToBottom(); + this.toDisposeBeforeUnmount.pushAll([ + this.props.clearConsoleEvent(() => + this.setState({ lines: [], charCount: 0 }) + ), + ]); + } + + override 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.listRef.current.scrollToItem(this.state.lines.length, 'end'); + } + }).bind(this); +} + +// const _Row = ({ +// index, +// style, +// data, +// }: { +// index: number; +// style: any; +// data: { lines: Line[]; timestamp: boolean }; +// }) => { +// return ( +// (data.lines[index].lineLen && ( +//
+//
+//           {data.lines[index].message}
+//         
+//
+// )) || +// null +// ); +// }; +// const Row = React.memo(_Row, areEqual); + +export namespace DecodeOutput { + export interface Props { + readonly clearConsoleEvent: Event; + readonly height: number; + } + + export interface State { + lines: Line[]; + charCount: number; + text: string; + } + + export interface SelectOption { + readonly label: string; + readonly value: T; + } + + export const MAX_CHARACTERS = 1_000_000; +} + + diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-send-input.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-send-input.tsx new file mode 100644 index 000000000..fd59277e8 --- /dev/null +++ b/arduino-ide-extension/src/browser/serial/decode/decode-send-input.tsx @@ -0,0 +1,135 @@ +import * as React from '@theia/core/shared/react'; +import { Key, KeyCode } from '@theia/core/lib/browser/keys'; +import { DisposableCollection } from '@theia/core/lib/common'; + +class HistoryList { + private readonly items: string[] = []; + private index = -1; + + constructor(private readonly size = 100) {} + + push(val: string): void { + if (val !== this.items[this.items.length - 1]) { + this.items.push(val); + } + while (this.items.length > this.size) { + this.items.shift(); + } + this.index = -1; + } + + previous(): string { + if (this.index === -1) { + this.index = this.items.length - 1; + return this.items[this.index]; + } + if (this.hasPrevious) { + return this.items[--this.index]; + } + return this.items[this.index]; + } + + private get hasPrevious(): boolean { + return this.index >= 1; + } + + next(): string { + if (this.index === this.items.length - 1) { + this.index = -1; + return ''; + } + if (this.hasNext) { + return this.items[++this.index]; + } + return ''; + } + + private get hasNext(): boolean { + return this.index >= 0 && this.index !== this.items.length - 1; + } +} + +export namespace DecodeSendInput { + export interface Props { + readonly onSend: (text: string) => void; + readonly resolveFocus: (element: HTMLElement | undefined) => void; + } + export interface State { + text: string; + history: HistoryList; + } +} + +export class DecodeSendInput extends React.Component< + DecodeSendInput.Props, + DecodeSendInput.State +> { + protected toDisposeBeforeUnmount = new DisposableCollection(); + + constructor(props: Readonly) { + super(props); + this.state = { text: '', history: new HistoryList() }; + this.onChange = this.onChange.bind(this); + this.onSend = this.onSend.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + } + + override componentWillUnmount(): void { + // TODO: "Your preferred browser's local storage is almost full." Discard `content` before saving layout? + this.toDisposeBeforeUnmount.dispose(); + } + + override render(): React.ReactNode { + return ( + + ); + } + + protected get placeholder(): string { + + return 'Enter backtrace text to decode. (Ex: Backtrace: 0x40086e7c:0x3ffb4ff0...)'; + } + + protected setRef = (element: HTMLElement | null): void => { + if (this.props.resolveFocus) { + this.props.resolveFocus(element || undefined); + } + }; + + protected onChange(event: React.ChangeEvent): void { + this.setState({ text: event.target.value }); + } + + protected onSend(): void { + this.props.onSend(this.state.text); + this.setState({ text: '' }); + } + + protected onKeyDown(event: React.KeyboardEvent): void { + const keyCode = KeyCode.createKeyCode(event.nativeEvent); + if (keyCode) { + const { key } = keyCode; + if (key === Key.ENTER) { + const { text } = this.state; + this.onSend(); + if (text) { + this.state.history.push(text); + } + } else if (key === Key.ARROW_UP) { + this.setState({ text: this.state.history.previous() }); + } else if (key === Key.ARROW_DOWN) { + this.setState({ text: this.state.history.next() }); + } else if (key === Key.ESCAPE) { + this.setState({ text: '' }); + } + } + } +} diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-view.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-view.tsx new file mode 100644 index 000000000..fb084b590 --- /dev/null +++ b/arduino-ide-extension/src/browser/serial/decode/decode-view.tsx @@ -0,0 +1,110 @@ +import { injectable } from '@theia/core/shared/inversify'; +import { AbstractViewContribution, codicon } from '@theia/core/lib/browser'; +import { DecodeWidget } from './decode-widget'; +import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core'; +import { + TabBarToolbarContribution, + TabBarToolbarRegistry, +} from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { ArduinoToolbar } from '../../toolbar/arduino-toolbar'; +import { ArduinoMenus } from '../../menu/arduino-menus'; + +export namespace DebugBox { + export namespace Commands { + export const CLEAR_OUTPUT = Command.toLocalizedCommand( + { + id: 'debug-box-clear-output', + label: 'Clear Output', + iconClass: codicon('clear-all'), + }, + 'vscode/output.contribution/clearOutput.label' + ); + } +} + +@injectable() +export class DecodeViewContribution + extends AbstractViewContribution + implements TabBarToolbarContribution +{ + static readonly TOGGLE_DECODE_BOX = DecodeWidget.ID + ':toggle'; + static readonly TOGGLE_DECODE_BOX_TOOLBAR = + DecodeWidget.ID + ':toggle-toolbar'; + static readonly RESET_DECODE_BOX = DecodeWidget.ID + ':reset'; + + constructor() { + super({ + widgetId: DecodeWidget.ID, + widgetName: DecodeWidget.LABEL, + defaultWidgetOptions: { + area: 'bottom', + }, + toggleCommandId: DecodeViewContribution.TOGGLE_DECODE_BOX, + toggleKeybinding: 'CtrlCmd+Shift+D', + }); + } + + override registerMenus(menus: MenuModelRegistry): void { + if (this.toggleCommand) { + menus.registerMenuAction(ArduinoMenus.TOOLS__MAIN_GROUP, { + commandId: this.toggleCommand.id, + label: DecodeWidget.LABEL, + order: '6', + }); + } + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem({ + id: DebugBox.Commands.CLEAR_OUTPUT.id, + command: DebugBox.Commands.CLEAR_OUTPUT.id, + tooltip: 'Clear Output' + }); + } + + override registerCommands(commands: CommandRegistry): void { + commands.registerCommand(DebugBox.Commands.CLEAR_OUTPUT, { + isEnabled: (widget) => widget instanceof DecodeWidget, + isVisible: (widget) => widget instanceof DecodeWidget, + execute: (widget) => { + if (widget instanceof DecodeWidget) { + widget.clearConsole(); + } + }, + }); + if (this.toggleCommand) { + commands.registerCommand(this.toggleCommand, { + execute: () => this.toggle(), + }); + commands.registerCommand( + { id: DecodeViewContribution.TOGGLE_DECODE_BOX_TOOLBAR }, + { + isVisible: (widget) => + ArduinoToolbar.is(widget) && widget.side === 'right', + execute: () => this.toggle(), + } + ); + } + commands.registerCommand( + { id: DecodeViewContribution.RESET_DECODE_BOX }, + { execute: () => this.reset() } + ); + } + + protected async toggle(): Promise { + const widget = this.tryGetWidget(); + if (widget) { + widget.dispose(); + } else { + await this.openView({ activate: true, reveal: true }); + } + } + + protected async reset(): Promise { + const widget = this.tryGetWidget(); + if (widget) { + widget.dispose(); + await this.openView({ activate: true, reveal: true }); + } + } +} diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx new file mode 100644 index 000000000..8f5dbaff0 --- /dev/null +++ b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx @@ -0,0 +1,135 @@ +import * as React from '@theia/core/shared/react'; +import { injectable, inject } from '@theia/core/shared/inversify'; +import { Emitter } from '@theia/core/lib/common/event'; +import { + ReactWidget, + Message, + Widget, + MessageLoop, +} from '@theia/core/lib/browser/widgets'; +import { DecodeSendInput } from './decode-send-input'; +import { DecodeOutput } from './decode-output'; +import { spawnCommand } from '../../../node/exec-util'; +import { ConfigService } from '../../../common/protocol'; + + +@injectable() +export class DecodeWidget extends ReactWidget { + + @inject(ConfigService) + protected readonly configService: ConfigService; + + static readonly LABEL = 'Decode Box'; + static readonly ID = 'decode-box'; + protected widgetHeight: number; + protected text: string; + private decodeOutputElement: React.RefObject; + + /** + * 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 = DecodeWidget.ID; + this.title.label = DecodeWidget.LABEL; + this.title.iconClass = 'monitor-tab-icon'; + this.title.closable = true; + this.scrollOptions = undefined; + this.toDispose.push(this.clearOutputEmitter); + this.decodeOutputElement = React.createRef(); + } + + protected override onBeforeAttach(msg: Message): void { + this.update(); + } + + clearConsole(): void { + this.clearOutputEmitter.fire(undefined); + this.update(); + } + + override dispose(): void { + super.dispose(); + } + + protected override onCloseRequest(msg: Message): void { + this.closing = true; + super.onCloseRequest(msg); + } + + protected override 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 override onResize(msg: Widget.ResizeMessage): void { + super.onResize(msg); + this.widgetHeight = msg.height; + this.update(); + } + + protected override 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 render(): React.ReactNode { + + return ( +
+
+
+ +
+
+
+ +
+
+ ); + } + + protected readonly onSend = (value: string) => this.doSend(value); + protected async doSend(value: string) { + const configPath = await this.configService.getConfiguration(); + console.log('This is the config path', configPath); + const xtensaPath= '/Users/radurentea/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-addr2line'; + // Add logic here; value is the backtrace user copied + let result = await spawnCommand(`${xtensaPath}`, [ + '-pfiaC', + '-e', + '/Users/radurentea/Documents/Arduino/sketch_nov3a/build/esp32.esp32.esp32wroverkit/sketch_nov3a.ino.elf', + `"${value}"`, + ]); + this.decodeOutputElement.current.decodeText(result); + } +} \ No newline at end of file diff --git a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx index f9aba5ed4..a8bb69afa 100644 --- a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx @@ -17,9 +17,15 @@ import { nls } from '@theia/core/lib/common'; import { MonitorManagerProxyClient } from '../../../common/protocol'; import { MonitorModel } from '../../monitor-model'; import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider'; +import { ConfigService } from '../../../common/protocol'; + @injectable() export class MonitorWidget extends ReactWidget { + + @inject(ConfigService) + protected readonly configService: ConfigService; + static readonly LABEL = nls.localize( 'arduino/common/serialMonitor', 'Serial Monitor' @@ -228,6 +234,8 @@ export class MonitorWidget extends ReactWidget { protected readonly onSend = (value: string) => this.doSend(value); protected async doSend(value: string): Promise { + const configPath = await this.configService.getConfiguration(); + console.log('This is the config path', configPath); this.monitorManagerProxy.send(value); } From 7af31bf8e31120ddad5f54df40c6db79bf9798b2 Mon Sep 17 00:00:00 2001 From: Radu Date: Tue, 20 Dec 2022 09:54:22 +0200 Subject: [PATCH 2/6] Remove testing code --- .../src/browser/serial/monitor/monitor-widget.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx index a8bb69afa..f9aba5ed4 100644 --- a/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/monitor/monitor-widget.tsx @@ -17,15 +17,9 @@ import { nls } from '@theia/core/lib/common'; import { MonitorManagerProxyClient } from '../../../common/protocol'; import { MonitorModel } from '../../monitor-model'; import { MonitorSettings } from '../../../node/monitor-settings/monitor-settings-provider'; -import { ConfigService } from '../../../common/protocol'; - @injectable() export class MonitorWidget extends ReactWidget { - - @inject(ConfigService) - protected readonly configService: ConfigService; - static readonly LABEL = nls.localize( 'arduino/common/serialMonitor', 'Serial Monitor' @@ -234,8 +228,6 @@ export class MonitorWidget extends ReactWidget { protected readonly onSend = (value: string) => this.doSend(value); protected async doSend(value: string): Promise { - const configPath = await this.configService.getConfiguration(); - console.log('This is the config path', configPath); this.monitorManagerProxy.send(value); } From 0e91bc97898133fe685100a3d7065cfde98dbe92 Mon Sep 17 00:00:00 2001 From: Radu Date: Mon, 6 Feb 2023 13:02:07 +0200 Subject: [PATCH 3/6] Decode multiple addresses --- .../browser/arduino-ide-frontend-module.ts | 6 +- .../browser/serial/decode/decode-output.tsx | 11 ++- .../browser/serial/decode/decode-widget.tsx | 70 ++++++++++++++----- 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index 61e3306d5..e1d09917c 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -500,7 +500,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(WidgetFactory).toDynamicValue((context) => ({ id: DecodeWidget.ID, createWidget: () => { - return new DecodeWidget(); + return new DecodeWidget( + context.container.get(ConfigService), + context.container.get(BoardsServiceProvider), + context.container.get(SketchesServiceClientImpl) + ); }, })); diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx index 1ef41bcaa..0fd658e8a 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx @@ -25,10 +25,9 @@ export class DecodeOutput extends React.Component< }; } - decodeText = (value: string) => { - console.log("decodeText works!", value); - this.setState({text: value}); - } + decodeText = (value: string) => { + this.setState({ text: value }); + }; override render(): React.ReactNode { return ( @@ -48,7 +47,7 @@ export class DecodeOutput extends React.Component< // > // {Row} // -
{this.state.text}
+
{this.state.text}
); } @@ -118,5 +117,3 @@ export namespace DecodeOutput { export const MAX_CHARACTERS = 1_000_000; } - - diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx index 8f5dbaff0..ffe834fc7 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx @@ -1,6 +1,7 @@ import * as React from '@theia/core/shared/react'; import { injectable, inject } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; +import URI from '@theia/core/lib/common/uri'; import { ReactWidget, Message, @@ -11,14 +12,12 @@ import { DecodeSendInput } from './decode-send-input'; import { DecodeOutput } from './decode-output'; import { spawnCommand } from '../../../node/exec-util'; import { ConfigService } from '../../../common/protocol'; - +import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl'; +import { BoardsServiceProvider } from '../../boards/boards-service-provider'; @injectable() export class DecodeWidget extends ReactWidget { - @inject(ConfigService) - protected readonly configService: ConfigService; - static readonly LABEL = 'Decode Box'; static readonly ID = 'decode-box'; protected widgetHeight: number; @@ -36,7 +35,16 @@ export class DecodeWidget extends ReactWidget { protected closing = false; protected readonly clearOutputEmitter = new Emitter(); - constructor() { + constructor( + @inject(ConfigService) + protected readonly configService: ConfigService, + + @inject(BoardsServiceProvider) + protected readonly boardsServiceProvider: BoardsServiceProvider, + + @inject(SketchesServiceClientImpl) + protected readonly sketchServiceClient: SketchesServiceClientImpl, + ) { super(); this.id = DecodeWidget.ID; this.title.label = DecodeWidget.LABEL; @@ -120,16 +128,46 @@ export class DecodeWidget extends ReactWidget { protected readonly onSend = (value: string) => this.doSend(value); protected async doSend(value: string) { - const configPath = await this.configService.getConfiguration(); - console.log('This is the config path', configPath); - const xtensaPath= '/Users/radurentea/Library/Arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/gcc8_4_0-esp-2021r2-patch3/bin/xtensa-esp32-elf-addr2line'; - // Add logic here; value is the backtrace user copied - let result = await spawnCommand(`${xtensaPath}`, [ - '-pfiaC', - '-e', - '/Users/radurentea/Documents/Arduino/sketch_nov3a/build/esp32.esp32.esp32wroverkit/sketch_nov3a.ino.elf', - `"${value}"`, - ]); - this.decodeOutputElement.current.decodeText(result); + + const configPath = await this.configService.getConfiguration() + .then(({ dataDirUri }) => (new URI(dataDirUri)).path); + const boards = this.boardsServiceProvider.boardsConfig + const fqbn = boards.selectedBoard?.fqbn; + if(!fqbn) { + return + } + const selectedBoard = fqbn.split(':')[1]; + const currentSketch = await this.sketchServiceClient.currentSketch(); + if (!CurrentSketch.isValid(currentSketch)) { + return; + } + const sketchUri = (new URI(currentSketch.uri)).path; + const elfPath = `${sketchUri}/build/${fqbn.split(':').join('.')}/${currentSketch.name}.ino.elf`; + // * enters an unkown foldername, in this case the version of gcc + const xtensaPath= `${configPath}/packages/${selectedBoard}/tools/xtensa-${selectedBoard}-elf-gcc/\*/bin/xtensa-${selectedBoard}-elf-addr2line`; + // Add logic here; value is the backtrace user + + const regex = new RegExp(/[0-3]x([a-f]|[A-F]|[0-9]){8}/g); + + const arrAddresses = value.match(regex); + + if(!arrAddresses) { + return this.decodeOutputElement.current.decodeText('Provided format can not be decoded!'); + } + + let decodeResult = ''; + + for(let i=0;i Date: Thu, 16 Feb 2023 18:52:02 +0200 Subject: [PATCH 4/6] Emphasise important data --- .../browser/serial/decode/decode-output.tsx | 136 ++++++++++-------- .../browser/serial/decode/decode-widget.tsx | 21 ++- 2 files changed, 87 insertions(+), 70 deletions(-) diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx index 0fd658e8a..75dea2a44 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx @@ -1,9 +1,18 @@ import * as React from '@theia/core/shared/react'; import { Event } from '@theia/core/lib/common/event'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; -// import { areEqual } from 'react-window'; +import { spawnCommand } from '../../../node/exec-util'; export type Line = { message: string; lineLen: number }; +export type Element = { + address: string; + function: string; + path: { + value: string; + isLink: boolean; + } + lineNumber: string +}; export class DecodeOutput extends React.Component< DecodeOutput.Props, @@ -13,41 +22,83 @@ export class DecodeOutput extends React.Component< * Do not touch it. It is used to be able to "follow" the serial monitor log. */ protected toDisposeBeforeUnmount = new DisposableCollection(); - private listRef: React.RefObject; constructor(props: Readonly) { super(props); - this.listRef = React.createRef(); this.state = { - lines: [], - charCount: 0, - text: '', + elements: [], }; } - decodeText = (value: string) => { - this.setState({ text: value }); + isClientPath = async (path:string): Promise => { + return await spawnCommand("cd", [ + path + ], (err) => err) + .then((data) => true) + .catch(err => false) + } + + openFinder = async (path:string) => { + await spawnCommand("open", [ + path + ]); + } + + retrievePath = (dirPath:string) => { + return dirPath.substring(0,dirPath.lastIndexOf("/")+1); + } + + decodeText = async (value: string) => { + const lines = value.split("\n"); + + // Remove the extra newline at the end + lines.pop(); + const elements : Array = []; + for(let i=0;i - // {Row} - // -
{this.state.text}
+
+ {this.state.elements.map((element) => ( +
+ {element.address} + {element.function} + at + { element.path.isLink ? await this.openFinder(this.retrievePath(element.path.value))}>{element.path.value} : {element.path.value} } + line + {element.lineNumber} +
+ ))} +
); } @@ -56,10 +107,9 @@ export class DecodeOutput extends React.Component< } override componentDidMount(): void { - this.scrollToBottom(); this.toDisposeBeforeUnmount.pushAll([ this.props.clearConsoleEvent(() => - this.setState({ lines: [], charCount: 0 }) + this.setState({ elements: [] }) ), ]); } @@ -68,36 +118,8 @@ export class DecodeOutput extends React.Component< // 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.listRef.current.scrollToItem(this.state.lines.length, 'end'); - } - }).bind(this); } -// const _Row = ({ -// index, -// style, -// data, -// }: { -// index: number; -// style: any; -// data: { lines: Line[]; timestamp: boolean }; -// }) => { -// return ( -// (data.lines[index].lineLen && ( -//
-//
-//           {data.lines[index].message}
-//         
-//
-// )) || -// null -// ); -// }; -// const Row = React.memo(_Row, areEqual); - export namespace DecodeOutput { export interface Props { readonly clearConsoleEvent: Event; @@ -105,9 +127,7 @@ export namespace DecodeOutput { } export interface State { - lines: Line[]; - charCount: number; - text: string; + elements: Element[]; } export interface SelectOption { diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx index ffe834fc7..becad6d82 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx @@ -127,8 +127,8 @@ export class DecodeWidget extends ReactWidget { } protected readonly onSend = (value: string) => this.doSend(value); + protected async doSend(value: string) { - const configPath = await this.configService.getConfiguration() .then(({ dataDirUri }) => (new URI(dataDirUri)).path); const boards = this.boardsServiceProvider.boardsConfig @@ -143,31 +143,28 @@ export class DecodeWidget extends ReactWidget { } const sketchUri = (new URI(currentSketch.uri)).path; const elfPath = `${sketchUri}/build/${fqbn.split(':').join('.')}/${currentSketch.name}.ino.elf`; + // * enters an unkown foldername, in this case the version of gcc const xtensaPath= `${configPath}/packages/${selectedBoard}/tools/xtensa-${selectedBoard}-elf-gcc/\*/bin/xtensa-${selectedBoard}-elf-addr2line`; - // Add logic here; value is the backtrace user - - const regex = new RegExp(/[0-3]x([a-f]|[A-F]|[0-9]){8}/g); - + const regex = new RegExp(/0x4(\d|[a-f]|[A-F]){7}/g); const arrAddresses = value.match(regex); - if(!arrAddresses) { return this.decodeOutputElement.current.decodeText('Provided format can not be decoded!'); } - let decodeResult = ''; - - for(let i=0;i Date: Sat, 18 Feb 2023 23:35:45 +0200 Subject: [PATCH 5/6] fix: Resolve conflicts --- .../src/browser/serial/decode/decode-widget.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx index becad6d82..8449bbe78 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-widget.tsx @@ -12,7 +12,7 @@ import { DecodeSendInput } from './decode-send-input'; import { DecodeOutput } from './decode-output'; import { spawnCommand } from '../../../node/exec-util'; import { ConfigService } from '../../../common/protocol'; -import { CurrentSketch, SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl'; +import { CurrentSketch, SketchesServiceClientImpl } from '../../sketches-service-client-impl'; import { BoardsServiceProvider } from '../../boards/boards-service-provider'; @injectable() @@ -130,7 +130,7 @@ export class DecodeWidget extends ReactWidget { protected async doSend(value: string) { const configPath = await this.configService.getConfiguration() - .then(({ dataDirUri }) => (new URI(dataDirUri)).path); + .then(({config}) => (new URI(config?.dataDirUri)).path); const boards = this.boardsServiceProvider.boardsConfig const fqbn = boards.selectedBoard?.fqbn; if(!fqbn) { From cfd71a45956a1998718e1d45bd268ba743c65eb1 Mon Sep 17 00:00:00 2001 From: Radu Date: Wed, 8 Mar 2023 16:24:50 +0200 Subject: [PATCH 6/6] fix: different version of tool I found a bug in which, the version of the tool in Arduino15 folder was different from the version the serial monitor was outputting, which would make the link unvalid (unreachable) --- .../src/browser/serial/decode/decode-output.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx index 75dea2a44..ebcd0e314 100644 --- a/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx +++ b/arduino-ide-extension/src/browser/serial/decode/decode-output.tsx @@ -30,6 +30,16 @@ export class DecodeOutput extends React.Component< }; } + // If a string of .. or . is found, replaces it with "*" + changeVersionToAny = (path: string) => { + const regex = new RegExp(/(\d\.\d\.\d)|(\d\.\d)/g); + const found = path.match(regex); + if(found) { + return path.replace(found[0], "*") + } + return path + } + isClientPath = async (path:string): Promise => { return await spawnCommand("cd", [ path @@ -60,6 +70,7 @@ export class DecodeOutput extends React.Component< line.shift(); } let pathSplit = line[3].split(":"); + pathSplit[0] = this.changeVersionToAny(pathSplit[0]); let obj: Element = { address: line[0], function: line[1],