Skip to content
This repository was archived by the owner on Dec 23, 2021. It is now read-only.

Debug Adapter Tracker Implementation #217

Merged
merged 16 commits into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/debugger/debugAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { DebugAdapterTracker, DebugConsole, DebugSession } from "vscode";
import { MessagingService } from "../service/messagingService";
import { DEBUG_COMMANDS } from "../view/constants";

export class DebugAdapter implements DebugAdapterTracker {
private readonly console: DebugConsole | undefined;
private readonly messagingService: MessagingService;
constructor(
debugSession: DebugSession,
messagingService: MessagingService
) {
this.console = debugSession.configuration.console;
this.messagingService = messagingService;
}
onWillStartSession() {
// To Implement
}
onWillReceiveMessage(message: any): void {
if (message.command) {
// Only send pertinent debug messages
switch (message.command) {
case DEBUG_COMMANDS.CONTINUE:
this.messagingService.sendStartMessage();
break;
case DEBUG_COMMANDS.STACK_TRACE:
this.messagingService.sendPauseMessage();
}
}
}
// A debugger error should unlock the webview
onError() {
this.messagingService.sendStartMessage();
}
// Device is always running when exiting debugging mode
onExit() {
this.messagingService.sendStartMessage();
}
}
25 changes: 25 additions & 0 deletions src/debugger/debugAdapterFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
DebugAdapterTracker,
DebugAdapterTrackerFactory,
DebugSession,
ProviderResult,
} from "vscode";
import { MessagingService } from "../service/messagingService";
import { DebugAdapter } from "./debugAdapter";

export class DebugAdapterFactory implements DebugAdapterTrackerFactory {
private debugSession: DebugSession;
private messagingService: MessagingService;
constructor(
debugSession: DebugSession,
messagingService: MessagingService
) {
this.debugSession = debugSession;
this.messagingService = messagingService;
}
public createDebugAdapterTracker(
session: DebugSession
): ProviderResult<DebugAdapterTracker> {
return new DebugAdapter(session, this.messagingService);
}
}
2 changes: 1 addition & 1 deletion src/debuggerCommunicationServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class DebuggerCommunicationServer {
private simulatorWebview: WebviewPanel | undefined;
private currentActiveDevice;
private isPendingResponse = false;
private pendingCallbacks: Array<Function> = [];
private pendingCallbacks: Function[] = [];

constructor(
webviewPanel: WebviewPanel | undefined,
Expand Down
13 changes: 13 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import {
TelemetryEventName,
} from "./constants";
import { CPXWorkspace } from "./cpxWorkspace";
import { DebugAdapterFactory } from "./debugger/debugAdapterFactory";
import { DebuggerCommunicationServer } from "./debuggerCommunicationServer";
import * as utils from "./extension_utils/utils";
import { SerialMonitor } from "./serialMonitor";
import { MessagingService } from "./service/messagingService";
import { SimulatorDebugConfigurationProvider } from "./simulatorDebugConfigurationProvider";
import TelemetryAI from "./telemetry/telemetryAI";
import { UsbDetector } from "./usbDetector";
Expand All @@ -35,6 +37,7 @@ let debuggerCommunicationHandler: DebuggerCommunicationServer;
let firstTimeClosed: boolean = true;
let shouldShowInvalidFileNamePopup: boolean = true;
let shouldShowRunCodePopup: boolean = true;
const messagingService = new MessagingService();

let currentActiveDevice: string = DEFAULT_DEVICE;

Expand Down Expand Up @@ -121,6 +124,7 @@ export async function activate(context: vscode.ExtensionContext) {

const openWebview = () => {
if (currentPanel) {
messagingService.setWebview(currentPanel.webview);
currentPanel.reveal(vscode.ViewColumn.Beside);
} else {
currentPanel = vscode.window.createWebviewPanel(
Expand All @@ -142,6 +146,7 @@ export async function activate(context: vscode.ExtensionContext) {
);

currentPanel.webview.html = getWebviewContent(context);
messagingService.setWebview(currentPanel.webview);

if (messageListener !== undefined) {
messageListener.dispose();
Expand Down Expand Up @@ -914,6 +919,14 @@ export async function activate(context: vscode.ExtensionContext) {
utils.getPathToScript(context, "out/", "debug_user_code.py")
);

const debugAdapterFactory = new DebugAdapterFactory(
vscode.debug.activeDebugSession,
messagingService
);
vscode.debug.registerDebugAdapterTrackerFactory(
"python",
debugAdapterFactory
);
// On Debug Session Start: Init comunication
const debugSessionsStarted = vscode.debug.onDidStartDebugSession(() => {
if (simulatorDebugConfiguration.deviceSimulatorExpressDebug) {
Expand Down
26 changes: 26 additions & 0 deletions src/service/messagingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Webview } from "vscode";
import { VSCODE_MESSAGES_TO_WEBVIEW } from "../view/constants";
export class MessagingService {
private currentWebviewTarget: Webview | undefined;

public setWebview(webview: Webview) {
this.currentWebviewTarget = webview;
}

// Send a message to webview if it exists
public sendMessageToWebview(debugCommand: string, state: Object) {
if (this.currentWebviewTarget) {
this.currentWebviewTarget.postMessage({ command: debugCommand });
}
}
public sendStartMessage() {
this.currentWebviewTarget.postMessage({
command: VSCODE_MESSAGES_TO_WEBVIEW.RUN_DEVICE,
});
}
public sendPauseMessage() {
this.currentWebviewTarget.postMessage({
command: VSCODE_MESSAGES_TO_WEBVIEW.PAUSE_DEVICE,
});
}
}
34 changes: 26 additions & 8 deletions src/view/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@

import * as React from "react";
import "./App.css";
import { DEVICE_LIST_KEY, VSCODE_MESSAGES_TO_WEBVIEW } from "./constants";
import {
DEVICE_LIST_KEY,
VIEW_STATE,
VSCODE_MESSAGES_TO_WEBVIEW,
} from "./constants";
import { Device } from "./container/device/Device";
import { ViewStateContext } from "./context";

interface IState {
currentDevice: string;
viewState: VIEW_STATE;
}

const defaultState = {
currentDevice: DEVICE_LIST_KEY.CPX,
viewState: VIEW_STATE.RUNNING,
};

class App extends React.Component<{}, IState> {
Expand Down Expand Up @@ -39,20 +46,31 @@ class App extends React.Component<{}, IState> {
return (
<div className="App">
<main className="App-main">
<Device currentSelectedDevice={this.state.currentDevice} />
<ViewStateContext.Provider value={this.state.viewState}>
<Device
currentSelectedDevice={this.state.currentDevice}
/>
</ViewStateContext.Provider>
</main>
</div>
);
}

handleMessage = (event: any): void => {
const message = event.data;
console.log(JSON.stringify(message));
if (
message.command === VSCODE_MESSAGES_TO_WEBVIEW.SET_DEVICE &&
message.active_device !== this.state.currentDevice
) {
this.setState({ currentDevice: message.active_device });

switch (message.command) {
case VSCODE_MESSAGES_TO_WEBVIEW.SET_DEVICE:
if (message.active_device !== this.state.currentDevice) {
this.setState({ currentDevice: message.active_device });
}
break;
case VSCODE_MESSAGES_TO_WEBVIEW.RUN_DEVICE:
this.setState({ viewState: VIEW_STATE.RUNNING });
break;
case VSCODE_MESSAGES_TO_WEBVIEW.PAUSE_DEVICE:
this.setState({ viewState: VIEW_STATE.PAUSE });
break;
}
};
}
Expand Down
4 changes: 0 additions & 4 deletions src/view/components/cpx/CpxSimulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,6 @@ class Simulator extends React.Component<{}, IState> {
running_file: message.state.running_file,
});
break;
default:
console.log("Invalid message received from the extension.");
this.setState({ ...this.state, cpx: DEFAULT_CPX_STATE });
break;
}
};

Expand Down
38 changes: 35 additions & 3 deletions src/view/components/microbit/MicrobitImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// Licensed under the MIT license.

import * as React from "react";
import { VIEW_STATE } from "../../constants";
import { ViewStateContext } from "../../context";
import "../../styles/Microbit.css";
import { MicrobitSvg } from "./Microbit_svg";
import { IRefObject, MicrobitSvg } from "./Microbit_svg";

interface EventTriggers {
onMouseUp: (event: Event, buttonKey: string) => void;
Expand All @@ -15,6 +17,11 @@ interface IProps {
leds: number[][];
}

const BUTTON_CLASSNAME = {
ACTIVE: "sim-button-outer",
DEACTIVATED: "sim-button-deactivated",
};

// Displays the SVG and call necessary svg modification.
export class MicrobitImage extends React.Component<IProps, {}> {
private svgRef: React.RefObject<MicrobitSvg> = React.createRef();
Expand All @@ -31,17 +38,28 @@ export class MicrobitImage extends React.Component<IProps, {}> {
componentDidUpdate() {
if (this.svgRef.current) {
updateAllLeds(this.props.leds, this.svgRef.current.getLeds());
if (this.context === VIEW_STATE.PAUSE) {
disableAllButtons(this.svgRef.current.getButtons());
} else if (this.context === VIEW_STATE.RUNNING) {
setupAllButtons(
this.props.eventTriggers,
this.svgRef.current.getButtons()
);
}
}
}
render() {
return <MicrobitSvg ref={this.svgRef} />;
}
}

MicrobitImage.contextType = ViewStateContext;
const setupButton = (
buttonElement: HTMLElement,
buttonElement: SVGRectElement,
eventTriggers: EventTriggers,
key: string
) => {
buttonElement.setAttribute("class", BUTTON_CLASSNAME.ACTIVE);
buttonElement.onmousedown = e => {
eventTriggers.onMouseDown(e, key);
};
Expand All @@ -52,13 +70,27 @@ const setupButton = (
eventTriggers.onMouseLeave(e, key);
};
};
const setupAllButtons = (eventTriggers: EventTriggers, buttonRefs: Object) => {
const setupAllButtons = (
eventTriggers: EventTriggers,
buttonRefs: IRefObject
) => {
for (const [key, ref] of Object.entries(buttonRefs)) {
if (ref.current) {
setupButton(ref.current, eventTriggers, key);
}
}
};
const disableAllButtons = (buttonRefs: IRefObject) => {
for (const [, ref] of Object.entries(buttonRefs)) {
if (ref.current) {
// to implement
ref.current.onmousedown = null;
ref.current.onmouseup = null;
ref.current.onmouseleave = null;
ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED);
}
}
};
const updateAllLeds = (
leds: number[][],
ledRefs: Array<Array<React.RefObject<SVGRectElement>>>
Expand Down
3 changes: 0 additions & 3 deletions src/view/components/microbit/MicrobitSimulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ export class MicrobitSimulator extends React.Component<any, IState> {
running_file: message.state.running_file,
});
break;
default:
console.log("Invalid message received from the extension.");
break;
}
};
componentDidMount() {
Expand Down
2 changes: 1 addition & 1 deletion src/view/components/microbit/Microbit_svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Adapted from : https://makecode.microbit.org/#editor

import * as React from "react";
interface IRefObject {
export interface IRefObject {
[key: string]: React.RefObject<SVGRectElement>;
}
/* tslint:disable */
Expand Down
16 changes: 5 additions & 11 deletions src/view/components/toolbar/InputSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// Licensed under the MIT license.

import * as React from "react";
import { WEBVIEW_MESSAGES } from "../../constants";
import { VIEW_STATE, WEBVIEW_MESSAGES } from "../../constants";
import { ViewStateContext } from "../../context";
import "../../styles/InputSlider.css";
import { sendMessage } from "../../utils/MessageUtils";
import { ISliderProps } from "../../viewUtils";
Expand All @@ -24,20 +25,10 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
case "reset-state":
this.setState({ value: 0 });
break;
case "set-state":
console.log(
"Setting the state: " + JSON.stringify(message.state)
);
break;
default:
console.log("Invalid message received from the extension.");
this.setState({ value: 0 });
break;
}
};

componentDidMount() {
console.log("Mounted");
window.addEventListener("message", this.handleMessage);
}

Expand All @@ -46,6 +37,7 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
window.removeEventListener("message", this.handleMessage);
}
render() {
const isInputDisabled = this.context === VIEW_STATE.PAUSE;
return (
<div className="inputSlider">
<span>{this.props.axisLabel}</span>
Expand Down Expand Up @@ -78,6 +70,7 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
value={this.state.value}
aria-label={`${this.props.type} sensor slider`}
defaultValue={this.props.minValue.toLocaleString()}
disabled={isInputDisabled}
/>
<span className="downLabelArea">
<span className="minLabel">{this.props.minLabel}</span>
Expand Down Expand Up @@ -131,5 +124,6 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
return valueInt;
};
}
InputSlider.contextType = ViewStateContext;

export default InputSlider;
Loading