Skip to content

Commit 3b48b2c

Browse files
authored
Debugger tool (#4717)
* Add ability to set/remove debugger statements in jseditor * made highlights darker, and only on current block being executed * restructure ast2blocklist config and add debugger block for conversion * Update status block behavior * Add debug block and its behavior * Update code2block unit test
1 parent 3453c18 commit 3b48b2c

File tree

13 files changed

+755
-893
lines changed

13 files changed

+755
-893
lines changed

js/activity.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ class Activity {
253253

254254
this.firstTimeUser = false;
255255
this.beginnerMode = false;
256+
this.runMode = "normal";
256257

257258
// Flag to disable keyboard during loading of MB
258259
this.keyboardEnableFlag;
@@ -1497,6 +1498,7 @@ class Activity {
14971498
* @param env {specifies environment}
14981499
*/
14991500
const doFastButton = (activity, env) => {
1501+
activity.runMode = "normal";
15001502
activity._doFastButton(env);
15011503
};
15021504

@@ -1732,6 +1734,7 @@ class Activity {
17321734
* Runs Music Blocks at a slower rate
17331735
*/
17341736
const doSlowButton = (activity) => {
1737+
activity.runMode = "slow";
17351738
activity._doSlowButton();
17361739
};
17371740

@@ -1758,6 +1761,7 @@ class Activity {
17581761
* Runs music blocks step by step
17591762
*/
17601763
const doStepButton = (activity) => {
1764+
activity.runMode = "step";
17611765
activity._doStepButton();
17621766
};
17631767

js/block.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,8 @@ class Block {
28462846
if (!that.blocks.getLongPressStatus() && !that.blocks.stageClick) {
28472847
topBlk = that.blocks.findTopBlock(thisBlock);
28482848

2849+
that.activity.runMode = "click";
2850+
that.activity.logo.turtleDelay = that.activity.DEFAULTDELAY;
28492851
that.activity.logo.synth.resume();
28502852

28512853
if (that.activity.turtles.running()) {
@@ -2863,6 +2865,8 @@ class Block {
28632865
if (!that.blocks.getLongPressStatus() && !that.blocks.stageClick) {
28642866
topBlk = that.blocks.findTopBlock(thisBlock);
28652867

2868+
that.activity.runMode = "click";
2869+
that.activity.logo.turtleDelay = that.activity.DEFAULTDELAY;
28662870
that.activity.logo.synth.resume();
28672871

28682872
if (that.activity.turtles.running()) {

js/blocks.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,12 @@ class Blocks {
21442144
this.isBlockMoving = false;
21452145
this.adjustExpandableClampBlock();
21462146
this.activity.refreshCanvas();
2147+
2148+
if (this.activity.turtles.running()) {
2149+
this.activity.logo.doStopTurtles();
2150+
const stopBtn = document.getElementById("stop");
2151+
if (stopBtn) stopBtn.style.color = "white";
2152+
}
21472153
};
21482154

21492155
/**
@@ -6960,16 +6966,24 @@ class Blocks {
69606966
}
69616967
}
69626968

6963-
if (typeof value === "string") {
6969+
// Comprehensive safety check for all value types
6970+
if (value === null || value === undefined) {
6971+
this.blockList[blk].text.text = "";
6972+
} else if (typeof value === "string") {
69646973
if (value.length > 6) {
69656974
value = value.substr(0, 5) + "...";
69666975
}
6967-
69686976
this.blockList[blk].text.text = value;
69696977
} else if (name === "divide") {
69706978
this.blockList[blk].text.text = mixedNumber(value);
69716979
} else {
6972-
this.blockList[blk].text.text = value.toString();
6980+
// Safe toString conversion
6981+
try {
6982+
this.blockList[blk].text.text = value.toString();
6983+
} catch (error) {
6984+
console.warn("Error converting value to string:", value, error);
6985+
this.blockList[blk].text.text = "";
6986+
}
69736987
}
69746988

69756989
this.blockList[blk].container.updateCache();

js/blocks/ExtrasBlocks.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
global
1414
_, last, FlowBlock, ValueBlock, LeftBlock, NOINPUTERRORMSG,
1515
NANERRORMSG, mixedNumber, TONEBPM, DEFAULTDELAY, Singer,
16-
StackClampBlock, platformColor
16+
StackClampBlock, platformColor, StatusMatrix
1717
*/
1818

1919
/* exported setupExtrasBlocks */
@@ -840,6 +840,51 @@ function setupExtrasBlocks(activity) {
840840
}
841841
}
842842

843+
/**
844+
* Represents a DebuggerBlock.
845+
* Extends FlowBlock.
846+
* @class
847+
* @extends FlowBlock
848+
*/
849+
class DebuggerBlock extends FlowBlock {
850+
/**
851+
* Creates an instance of DebuggerBlock.
852+
*/
853+
constructor() {
854+
super("debugger", _("debugger"));
855+
this.setPalette("extras", activity);
856+
this.setHelpString([
857+
_("The Debug block adds a debug point in your code. When reached, execution will pause and you can inspect variables."),
858+
"documentation",
859+
"",
860+
"debugger"
861+
]);
862+
863+
this.formBlock({
864+
name: _("debugger")
865+
});
866+
}
867+
868+
/**
869+
* Handles the flow of the DebuggerBlock.
870+
* @param {Array} args - The arguments passed to the block.
871+
* @param {Logo} logo - The Logo interpreter instance.
872+
* @param {number} turtle - The turtle associated with the block.
873+
* @param {Block} blk - The block instance.
874+
*/
875+
flow(args, logo, turtle, blk) {
876+
const mode = activity.runMode;
877+
878+
// Only run debugger statement if normal playmode (final product)
879+
// All other run methods are for debugging only and should be used as such
880+
if (mode !== "slow" && mode !== "step" && mode !== "click") {
881+
return;
882+
}
883+
// Stop all turtles and pause execution
884+
activity.logo.turtleDelay = -1;
885+
}
886+
}
887+
843888
new SaveABCBlock().setup(activity);
844889
new SaveLilypondBlock().setup(activity);
845890
new SaveSVGBlock().setup(activity);
@@ -855,6 +900,7 @@ function setupExtrasBlocks(activity) {
855900
new WaitBlock().setup(activity);
856901
new CommentBlock().setup(activity);
857902
new PrintBlock().setup(activity);
903+
new DebuggerBlock().setup(activity);
858904
// NOP blocks
859905
new NOPValueBlock().setup(activity);
860906
new NOPOneArgMathBlock().setup(activity);

js/blocks/WidgetBlocks.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,22 +1545,6 @@ function setupWidgetBlocks(activity) {
15451545
]);
15461546

15471547
this.formBlock({ name: _("status"), canCollapse: true });
1548-
this.makeMacro((x, y) => [
1549-
[0, "status", x, y, [null, 1, 11]],
1550-
[1, "hidden", 0, 0, [0, 10]],
1551-
[2, "print", 0, 0, [10, 3, 4]],
1552-
[3, "beatvalue", 0, 0, [2]],
1553-
[4, "print", 0, 0, [2, 5, 6]],
1554-
[5, "measurevalue", 0, 0, [4]],
1555-
[6, "print", 0, 0, [4, 7, 8]],
1556-
[7, "elapsednotes", 0, 0, [6]],
1557-
[8, "print", 0, 0, [6, 9, null]],
1558-
[9, "bpmfactor", 0, 0, [8]],
1559-
[10, "print", 0, 0, [1, 12, 2]],
1560-
[11, "hiddennoflow", 0, 0, [0, null]],
1561-
[12, ["outputtools", { value: "letter class" }], 0, 0, [10, 13]],
1562-
[13, "currentpitch", 0, 0, [12]]
1563-
]);
15641548
}
15651549

15661550
/**

js/js-export/__tests__/ast2blocklist.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,16 +1153,16 @@ describe("AST2BlockList Class", () => {
11531153
[25, ["text", { "value": "action" }], 0, 0, [24]],
11541154
[26, "drift", 0, 0, [24, 27, null]],
11551155
[27, "onbeatdo", 0, 0, [26, 28, 29, 30]],
1156-
[28, ["namedbox", { "value": "BEATCOUNT" }], 0, 0, [27]],
1156+
[28, "nopValueBlock", 0, 0, [27]],
11571157
[29, ["text", { "value": "action" }], 0, 0, [27]],
11581158
[30, "onbeatdo", 0, 0, [27, 31, 32, 33]],
1159-
[31, ["namedbox", { "value": "MEASURECOUNT" }], 0, 0, [30]],
1159+
[31, "nopValueBlock", 0, 0, [30]],
11601160
[32, ["text", { "value": "action" }], 0, 0, [30]],
11611161
[33, "onbeatdo", 0, 0, [30, 34, 35, 36]],
1162-
[34, ["namedbox", { "value": "BPM" }], 0, 0, [33]],
1162+
[34, "nopValueBlock", 0, 0, [33]],
11631163
[35, ["text", { "value": "action" }], 0, 0, [33]],
11641164
[36, "onbeatdo", 0, 0, [33, 37, 38, 39]],
1165-
[37, ["namedbox", { "value": "BEATFACTOR" }], 0, 0, [36]],
1165+
[37, "beatfactor", 0, 0, [36]],
11661166
[38, ["text", { "value": "action" }], 0, 0, [36]],
11671167
[39, "everybeatdonew", 0, 0, [36, 40, 41]],
11681168
[40, ["text", { "value": "action" }], 0, 0, [39]],

js/js-export/ast2blocklist.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class AST2BlockList {
7373
for (let body of AST.body) {
7474
_createNodeAndAddToTree(body, root);
7575
}
76+
console.log(JSON.stringify(root["children"], null, 2));
7677
return root["children"];
7778

7879
//
@@ -137,6 +138,14 @@ class AST2BlockList {
137138
}
138139
}
139140
if (matched) {
141+
// If there's no name property but there is a name_map, try to get the name from the map
142+
if (entry.name_map) {
143+
const calleePropertyName = _getPropertyValue(bodyAST, "expression.argument.callee.property.name");
144+
if (calleePropertyName && entry.name_map[calleePropertyName]) {
145+
console.log(entry.name_map[calleePropertyName]);
146+
entry.name = entry.name_map[calleePropertyName];
147+
}
148+
}
140149
return entry;
141150
}
142151
}
@@ -294,7 +303,7 @@ class AST2BlockList {
294303
function _propertyOf(block) {
295304
const block_name = Array.isArray(block[1]) ? block[1][0] : block[1];
296305
for (const entry of config.body_blocks) {
297-
if ("name" in entry && entry.name === block_name) {
306+
if (("name" in entry && entry.name === block_name) || ("name_map" in entry && Object.values(entry.name_map).includes(block_name))) {
298307
// Use default_connections if blocklist_connections is not specified
299308
const connections = {
300309
count: entry.blocklist_connections ? entry.blocklist_connections.length : config.default_connections.length
@@ -315,7 +324,7 @@ class AST2BlockList {
315324

316325
const secondChildIndex = connectionsArray.indexOf("second_child");
317326
if (secondChildIndex !== -1) connections.second_child = secondChildIndex;
318-
327+
319328
// Use default_vspaces if not specified in the block
320329
const vspaces = entry.default_vspaces || config.default_vspaces || { body: 1 };
321330
return "body" in vspaces ? {
@@ -506,6 +515,14 @@ class AST2BlockList {
506515
if (entry.name === block_name) {
507516
blockConfig = entry;
508517
break;
518+
} else if (entry.name_map) {
519+
for (const name in entry.name_map) {
520+
if (block_name === entry.name_map[name]) {
521+
blockConfig = entry;
522+
console.log(entry);
523+
console.log(block_name);
524+
}
525+
}
509526
}
510527
}
511528

@@ -609,6 +626,9 @@ class AST2BlockList {
609626
connections[0] = parentBlockNumber;
610627
block.push(connections);
611628
vspaces = _addValueArgsToBlockList(arg.arguments, blockList, blockNumber);
629+
if (arg.arguments.length === 0) {
630+
vspaces += 1;
631+
}
612632
} else {
613633
throw new Error(`Unsupported value argument: ${arg}`);
614634
}

0 commit comments

Comments
 (0)