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

Commit 58e989e

Browse files
Debug Adapter Tracker Implementation (#217)
1 parent 389a2b2 commit 58e989e

14 files changed

+189
-35
lines changed

src/debugger/debugAdapter.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { DebugAdapterTracker, DebugConsole, DebugSession } from "vscode";
2+
import { MessagingService } from "../service/messagingService";
3+
import { DEBUG_COMMANDS } from "../view/constants";
4+
5+
export class DebugAdapter implements DebugAdapterTracker {
6+
private readonly console: DebugConsole | undefined;
7+
private readonly messagingService: MessagingService;
8+
constructor(
9+
debugSession: DebugSession,
10+
messagingService: MessagingService
11+
) {
12+
this.console = debugSession.configuration.console;
13+
this.messagingService = messagingService;
14+
}
15+
onWillStartSession() {
16+
// To Implement
17+
}
18+
onWillReceiveMessage(message: any): void {
19+
if (message.command) {
20+
// Only send pertinent debug messages
21+
switch (message.command) {
22+
case DEBUG_COMMANDS.CONTINUE:
23+
this.messagingService.sendStartMessage();
24+
break;
25+
case DEBUG_COMMANDS.STACK_TRACE:
26+
this.messagingService.sendPauseMessage();
27+
}
28+
}
29+
}
30+
// A debugger error should unlock the webview
31+
onError() {
32+
this.messagingService.sendStartMessage();
33+
}
34+
// Device is always running when exiting debugging mode
35+
onExit() {
36+
this.messagingService.sendStartMessage();
37+
}
38+
}

src/debugger/debugAdapterFactory.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {
2+
DebugAdapterTracker,
3+
DebugAdapterTrackerFactory,
4+
DebugSession,
5+
ProviderResult,
6+
} from "vscode";
7+
import { MessagingService } from "../service/messagingService";
8+
import { DebugAdapter } from "./debugAdapter";
9+
10+
export class DebugAdapterFactory implements DebugAdapterTrackerFactory {
11+
private debugSession: DebugSession;
12+
private messagingService: MessagingService;
13+
constructor(
14+
debugSession: DebugSession,
15+
messagingService: MessagingService
16+
) {
17+
this.debugSession = debugSession;
18+
this.messagingService = messagingService;
19+
}
20+
public createDebugAdapterTracker(
21+
session: DebugSession
22+
): ProviderResult<DebugAdapterTracker> {
23+
return new DebugAdapter(session, this.messagingService);
24+
}
25+
}

src/debuggerCommunicationServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class DebuggerCommunicationServer {
2626
private simulatorWebview: WebviewPanel | undefined;
2727
private currentActiveDevice;
2828
private isPendingResponse = false;
29-
private pendingCallbacks: Array<Function> = [];
29+
private pendingCallbacks: Function[] = [];
3030

3131
constructor(
3232
webviewPanel: WebviewPanel | undefined,

src/extension.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import {
1616
TelemetryEventName,
1717
} from "./constants";
1818
import { CPXWorkspace } from "./cpxWorkspace";
19+
import { DebugAdapterFactory } from "./debugger/debugAdapterFactory";
1920
import { DebuggerCommunicationServer } from "./debuggerCommunicationServer";
2021
import * as utils from "./extension_utils/utils";
2122
import { SerialMonitor } from "./serialMonitor";
23+
import { MessagingService } from "./service/messagingService";
2224
import { SimulatorDebugConfigurationProvider } from "./simulatorDebugConfigurationProvider";
2325
import TelemetryAI from "./telemetry/telemetryAI";
2426
import { UsbDetector } from "./usbDetector";
@@ -35,6 +37,7 @@ let debuggerCommunicationHandler: DebuggerCommunicationServer;
3537
let firstTimeClosed: boolean = true;
3638
let shouldShowInvalidFileNamePopup: boolean = true;
3739
let shouldShowRunCodePopup: boolean = true;
40+
const messagingService = new MessagingService();
3841

3942
let currentActiveDevice: string = DEFAULT_DEVICE;
4043

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

122125
const openWebview = () => {
123126
if (currentPanel) {
127+
messagingService.setWebview(currentPanel.webview);
124128
currentPanel.reveal(vscode.ViewColumn.Beside);
125129
} else {
126130
currentPanel = vscode.window.createWebviewPanel(
@@ -142,6 +146,7 @@ export async function activate(context: vscode.ExtensionContext) {
142146
);
143147

144148
currentPanel.webview.html = getWebviewContent(context);
149+
messagingService.setWebview(currentPanel.webview);
145150

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

922+
const debugAdapterFactory = new DebugAdapterFactory(
923+
vscode.debug.activeDebugSession,
924+
messagingService
925+
);
926+
vscode.debug.registerDebugAdapterTrackerFactory(
927+
"python",
928+
debugAdapterFactory
929+
);
917930
// On Debug Session Start: Init comunication
918931
const debugSessionsStarted = vscode.debug.onDidStartDebugSession(() => {
919932
if (simulatorDebugConfiguration.deviceSimulatorExpressDebug) {

src/service/messagingService.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Webview } from "vscode";
2+
import { VSCODE_MESSAGES_TO_WEBVIEW } from "../view/constants";
3+
export class MessagingService {
4+
private currentWebviewTarget: Webview | undefined;
5+
6+
public setWebview(webview: Webview) {
7+
this.currentWebviewTarget = webview;
8+
}
9+
10+
// Send a message to webview if it exists
11+
public sendMessageToWebview(debugCommand: string, state: Object) {
12+
if (this.currentWebviewTarget) {
13+
this.currentWebviewTarget.postMessage({ command: debugCommand });
14+
}
15+
}
16+
public sendStartMessage() {
17+
this.currentWebviewTarget.postMessage({
18+
command: VSCODE_MESSAGES_TO_WEBVIEW.RUN_DEVICE,
19+
});
20+
}
21+
public sendPauseMessage() {
22+
this.currentWebviewTarget.postMessage({
23+
command: VSCODE_MESSAGES_TO_WEBVIEW.PAUSE_DEVICE,
24+
});
25+
}
26+
}

src/view/App.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33

44
import * as React from "react";
55
import "./App.css";
6-
import { DEVICE_LIST_KEY, VSCODE_MESSAGES_TO_WEBVIEW } from "./constants";
6+
import {
7+
DEVICE_LIST_KEY,
8+
VIEW_STATE,
9+
VSCODE_MESSAGES_TO_WEBVIEW,
10+
} from "./constants";
711
import { Device } from "./container/device/Device";
12+
import { ViewStateContext } from "./context";
813

914
interface IState {
1015
currentDevice: string;
16+
viewState: VIEW_STATE;
1117
}
1218

1319
const defaultState = {
1420
currentDevice: DEVICE_LIST_KEY.CPX,
21+
viewState: VIEW_STATE.RUNNING,
1522
};
1623

1724
class App extends React.Component<{}, IState> {
@@ -39,20 +46,31 @@ class App extends React.Component<{}, IState> {
3946
return (
4047
<div className="App">
4148
<main className="App-main">
42-
<Device currentSelectedDevice={this.state.currentDevice} />
49+
<ViewStateContext.Provider value={this.state.viewState}>
50+
<Device
51+
currentSelectedDevice={this.state.currentDevice}
52+
/>
53+
</ViewStateContext.Provider>
4354
</main>
4455
</div>
4556
);
4657
}
4758

4859
handleMessage = (event: any): void => {
4960
const message = event.data;
50-
console.log(JSON.stringify(message));
51-
if (
52-
message.command === VSCODE_MESSAGES_TO_WEBVIEW.SET_DEVICE &&
53-
message.active_device !== this.state.currentDevice
54-
) {
55-
this.setState({ currentDevice: message.active_device });
61+
62+
switch (message.command) {
63+
case VSCODE_MESSAGES_TO_WEBVIEW.SET_DEVICE:
64+
if (message.active_device !== this.state.currentDevice) {
65+
this.setState({ currentDevice: message.active_device });
66+
}
67+
break;
68+
case VSCODE_MESSAGES_TO_WEBVIEW.RUN_DEVICE:
69+
this.setState({ viewState: VIEW_STATE.RUNNING });
70+
break;
71+
case VSCODE_MESSAGES_TO_WEBVIEW.PAUSE_DEVICE:
72+
this.setState({ viewState: VIEW_STATE.PAUSE });
73+
break;
5674
}
5775
};
5876
}

src/view/components/cpx/CpxSimulator.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,6 @@ class Simulator extends React.Component<{}, IState> {
117117
running_file: message.state.running_file,
118118
});
119119
break;
120-
default:
121-
console.log("Invalid message received from the extension.");
122-
this.setState({ ...this.state, cpx: DEFAULT_CPX_STATE });
123-
break;
124120
}
125121
};
126122

src/view/components/microbit/MicrobitImage.tsx

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// Licensed under the MIT license.
33

44
import * as React from "react";
5+
import { VIEW_STATE } from "../../constants";
6+
import { ViewStateContext } from "../../context";
57
import "../../styles/Microbit.css";
6-
import { MicrobitSvg } from "./Microbit_svg";
8+
import { IRefObject, MicrobitSvg } from "./Microbit_svg";
79

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

20+
const BUTTON_CLASSNAME = {
21+
ACTIVE: "sim-button-outer",
22+
DEACTIVATED: "sim-button-deactivated",
23+
};
24+
1825
// Displays the SVG and call necessary svg modification.
1926
export class MicrobitImage extends React.Component<IProps, {}> {
2027
private svgRef: React.RefObject<MicrobitSvg> = React.createRef();
@@ -31,17 +38,28 @@ export class MicrobitImage extends React.Component<IProps, {}> {
3138
componentDidUpdate() {
3239
if (this.svgRef.current) {
3340
updateAllLeds(this.props.leds, this.svgRef.current.getLeds());
41+
if (this.context === VIEW_STATE.PAUSE) {
42+
disableAllButtons(this.svgRef.current.getButtons());
43+
} else if (this.context === VIEW_STATE.RUNNING) {
44+
setupAllButtons(
45+
this.props.eventTriggers,
46+
this.svgRef.current.getButtons()
47+
);
48+
}
3449
}
3550
}
3651
render() {
3752
return <MicrobitSvg ref={this.svgRef} />;
3853
}
3954
}
55+
56+
MicrobitImage.contextType = ViewStateContext;
4057
const setupButton = (
41-
buttonElement: HTMLElement,
58+
buttonElement: SVGRectElement,
4259
eventTriggers: EventTriggers,
4360
key: string
4461
) => {
62+
buttonElement.setAttribute("class", BUTTON_CLASSNAME.ACTIVE);
4563
buttonElement.onmousedown = e => {
4664
eventTriggers.onMouseDown(e, key);
4765
};
@@ -52,13 +70,27 @@ const setupButton = (
5270
eventTriggers.onMouseLeave(e, key);
5371
};
5472
};
55-
const setupAllButtons = (eventTriggers: EventTriggers, buttonRefs: Object) => {
73+
const setupAllButtons = (
74+
eventTriggers: EventTriggers,
75+
buttonRefs: IRefObject
76+
) => {
5677
for (const [key, ref] of Object.entries(buttonRefs)) {
5778
if (ref.current) {
5879
setupButton(ref.current, eventTriggers, key);
5980
}
6081
}
6182
};
83+
const disableAllButtons = (buttonRefs: IRefObject) => {
84+
for (const [, ref] of Object.entries(buttonRefs)) {
85+
if (ref.current) {
86+
// to implement
87+
ref.current.onmousedown = null;
88+
ref.current.onmouseup = null;
89+
ref.current.onmouseleave = null;
90+
ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED);
91+
}
92+
}
93+
};
6294
const updateAllLeds = (
6395
leds: number[][],
6496
ledRefs: Array<Array<React.RefObject<SVGRectElement>>>

src/view/components/microbit/MicrobitSimulator.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,6 @@ export class MicrobitSimulator extends React.Component<any, IState> {
8282
running_file: message.state.running_file,
8383
});
8484
break;
85-
default:
86-
console.log("Invalid message received from the extension.");
87-
break;
8885
}
8986
};
9087
componentDidMount() {

src/view/components/microbit/Microbit_svg.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// Adapted from : https://makecode.microbit.org/#editor
55

66
import * as React from "react";
7-
interface IRefObject {
7+
export interface IRefObject {
88
[key: string]: React.RefObject<SVGRectElement>;
99
}
1010
/* tslint:disable */

src/view/components/toolbar/InputSlider.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// Licensed under the MIT license.
33

44
import * as React from "react";
5-
import { WEBVIEW_MESSAGES } from "../../constants";
5+
import { VIEW_STATE, WEBVIEW_MESSAGES } from "../../constants";
6+
import { ViewStateContext } from "../../context";
67
import "../../styles/InputSlider.css";
78
import { sendMessage } from "../../utils/MessageUtils";
89
import { ISliderProps } from "../../viewUtils";
@@ -24,20 +25,10 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
2425
case "reset-state":
2526
this.setState({ value: 0 });
2627
break;
27-
case "set-state":
28-
console.log(
29-
"Setting the state: " + JSON.stringify(message.state)
30-
);
31-
break;
32-
default:
33-
console.log("Invalid message received from the extension.");
34-
this.setState({ value: 0 });
35-
break;
3628
}
3729
};
3830

3931
componentDidMount() {
40-
console.log("Mounted");
4132
window.addEventListener("message", this.handleMessage);
4233
}
4334

@@ -46,6 +37,7 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
4637
window.removeEventListener("message", this.handleMessage);
4738
}
4839
render() {
40+
const isInputDisabled = this.context === VIEW_STATE.PAUSE;
4941
return (
5042
<div className="inputSlider">
5143
<span>{this.props.axisLabel}</span>
@@ -78,6 +70,7 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
7870
value={this.state.value}
7971
aria-label={`${this.props.type} sensor slider`}
8072
defaultValue={this.props.minValue.toLocaleString()}
73+
disabled={isInputDisabled}
8174
/>
8275
<span className="downLabelArea">
8376
<span className="minLabel">{this.props.minLabel}</span>
@@ -131,5 +124,6 @@ class InputSlider extends React.Component<ISliderProps, any, any> {
131124
return valueInt;
132125
};
133126
}
127+
InputSlider.contextType = ViewStateContext;
134128

135129
export default InputSlider;

0 commit comments

Comments
 (0)