Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LANGUAGE, UI] Integrate Skulpts debugger to Hedy #4465

Merged
merged 54 commits into from
Oct 30, 2023

Conversation

jpelay
Copy link
Member

@jpelay jpelay commented Sep 8, 2023

Description

Skulpt has an integrated debugger that is shipped as a separate entity and is envisioned to work like pdb. The basis that is there, however, can help us a great deal in getting our own debugger to work. For this, first we need to integrate the debugger that comes with Skulpt and edit it so it can work with our code. This will not affect Skulpt, since we are just extending it, so any problems that might come with the debugger will not affect the normal execution of code.

However, by doing this, we are taking the responsibility to maintain this piece of code, but I think is very much worth the cost.

The code in this PR will most likely be a little ugly since this is a proof of concept about getting this to work, and then I'll tackle the task of improving the code and making it adhere to our standards.

fixes #4427

@jpelay
Copy link
Member Author

jpelay commented Sep 8, 2023

It does execute code!

image

@Felienne
Copy link
Member

Felienne commented Sep 8, 2023

OMG I LOVE WE ARE EXPLORING THIS!!

I am ccing @juliabolt, she is also exploring more work on the debugger (since yesterday, so she is very new :)) It might make sense in a few weeks to meet (she might also drop into the contributors meeting in a while)

@jpelay
Copy link
Member Author

jpelay commented Sep 8, 2023

OMG I LOVE WE ARE EXPLORING THIS!!

I am ccing @juliabolt, she is also exploring more work on the debugger (since yesterday, so she is very new :)) It might make sense in a few weeks to meet (she might also drop into the contributors meeting in a while)

Great! Ok! I'm gonna keep working on this and will keep you updated. I also have a functioning version of the debugger from a past version of the Skulpt site, so I can provide that as well, if you'd like

@jpelay
Copy link
Member Author

jpelay commented Sep 9, 2023

It works now for print!

debugger-Hedy.mp4

@jpelay
Copy link
Member Author

jpelay commented Sep 9, 2023

Some bad news, the debugger doesn't work with the input function we supply to Skulpt, it only works when the input function is a simple alert from Javascript

debug.3.mp4

@Felienne
Copy link
Member

Felienne commented Sep 9, 2023

Some bad news, the debugger doesn't work with the input function we supply to Skulpt, it only works when the input function is a simple alert from Javascript

debug.3.mp4

Ah what a pity! Could we somehow catch the input and replace it with our box? Something like:

  • Show our box first, store inout
  • Run the simple alert, enter the stored input (without the user seeing)
  • Continue?

But I am not at all sure these things are feasible!

@jpelay
Copy link
Member Author

jpelay commented Sep 9, 2023

Ah what a pity! Could we somehow catch the input and replace it with our box? Something like:

  • Show our box first, store inout
  • Run the simple alert, enter the stored input (without the user seeing)
  • Continue?

But I am not at all sure these things are feasible!

That's a very good idea, not sure if it's feasible due to the program having to stop in order to do that, but I can try!

@juliabolt
Copy link
Collaborator

Thanks for tagging me Felienne! And thanks for the updates, I look forward to meeting you Jesus!

@jpelay
Copy link
Member Author

jpelay commented Sep 14, 2023

So I managed to fixed the problems and it works with ask now!

debugger.ask.woring.mp4

@jpelay
Copy link
Member Author

jpelay commented Sep 14, 2023

I need to work with the integration between the source mapper and the debugger, but if you hardcode the breakpoints, debugging nested code is possible!

debugger.repeat.block.mp4

@Felienne
Copy link
Member

I need to work with the integration between the source mapper and the debugger, but if you hardcode the breakpoints, debugging nested code is possible!

debugger.repeat.block.mp4

OOOWWW so great!!!! This is going to help students so much!!

@jpelay
Copy link
Member Author

jpelay commented Sep 14, 2023

I'm testing the debugger with each statement to see where I can place the breakpoints in a way that makes sense and works. So far everything works, except for sleeps after repeats. It even works for nested code like this, in the console you can see the current python line being executed:

debugging.nested.blocks.mp4

@jpelay
Copy link
Member Author

jpelay commented Sep 14, 2023

After testing the debugger with every statement in Hedy it works for almost everything, except the PyGame. Although I think I need to look into it more to decide definitely whether it works with the pygame or not. But it works for everything else!!! In every level!!!

@jpelay
Copy link
Member Author

jpelay commented Sep 15, 2023

Note to self: Need to change the way the editor handle runtime errors and mimic the way Skulpt handles them in a normal execution path.

@jpelay
Copy link
Member Author

jpelay commented Sep 17, 2023

Hi @Felienne! I have a question about how can we proceed with respect to marking in which Python lines to place the breakpoints. I have come out with two proposals:

  1. Mark the lines with a comment at compile time. This would consists on a flag being set in the ConvertToPython class, if the flag is set to true we would generate something like this:
__trtl = 100
try:
  __trtl = int(__trtl)
except ValueError:
  raise Exception(f'....')
t.forward(min(600, __trtl) if __trtl > 0 else max(-600, __trtl)) # ___BREAKPOINT___
time.sleep(0.1)
__trtl = 45
try:
  __trtl = int(__trtl)
except ValueError:
  raise Exception(f'...')
t.right(min(600, __trtl) if __trtl > 0 else max(-600, __trtl)) # ___BREAKPOINT___

This has the advantage of not needing any post processing in the Javascript, because we'd only need to iterate over the code, find the lines with # __BREAKPOINT__ in them, and set the breakpoint there.

However, I do understand that by doing this we can be adding a failure point in one of our most critical systems, possibly causing issues with the transpilation process if a bug is introduced.

  1. Process the lines in the front-end, deciding where to put the breakpoints by looking at the source map and then processing the Python code. In this case the source map would look like this:
    "0": {
        "hedy_range": {...},
        "python_range": {...},
        "error": null | exception
        "command": "var" | "assign" | "if" | "while" | "for" | ...
    }
}

Then, in Typescript, we'd iterate in the sourcemap, and depending on the level and which command we're processing, decide where to put the breakpoint. For example, in the case of a repeat on level 7:

if (statement.command === 'repeat) {
    python_code = getCodeFromSourceMap(statement.python_range) // get the Python lines as an array
    for (line in python_code) { 
        if (line.match('/for __i__ in range.+/')) {
            // place breakpoint in this line
        }
    }
}

Some commands, however, like the turtle commands or the if in level 5-7 in the source map appear as the rule command, having we to distinguish between them by processing their respectives strings.

As you can see this method involves more code and we'd be dealing with the Python code without knowing the structure of the underlying code, as opposed as doing it directly at compile time. We'd have to do a ton of string processing for each command of each level, at least when they change. Another downside of this method is that when changing the generated Python code in the transpiler, we'd have to edit this processing as well. However, this would mean that the transpiler code would be unchanged.

What do you think?

@Felienne
Copy link
Member

Felienne commented Sep 18, 2023

Hi @jpelay!

Thanks for the extensive explanation!! This really helped me to quickly understand the two options. I would propose option 1 for two reasons:

  1. The Python transpilation part (while being our most critical system as you say) is the best tested part of the code, I don't fear so much that it will break, and if it break, we will know soon from users. Breaking javascript is trickier to test and to detect for users too.
  2. In general, our line recently has been "more Python, less javascript" (with the HTMX change, for example) More things in one system for writing and testing helps to get a better grip on the code base.

From your text I also read (maybe in between the lines) that you'd prefer option 1? I see a lot of downsides mentioned in option 2. If your main worry for 1 is breaking the transpiler, I think we should take the risk because it is well tested and well understood.

@jpelay jpelay marked this pull request as ready for review October 29, 2023 02:04
@jpelay
Copy link
Member Author

jpelay commented Oct 29, 2023

This PR is now ready for review! There are some things that I want to improve and change, but I think we can leave that for a new PR (I need to have this finished so I can present it in my dissertation)

@Felienne
Copy link
Member

This seems to work GREAT from the front-end @jpelay!! Finally a debugger for if's and loops!!!!

One tiny UX issue I saw is that if you start the debugger, you get the confetti immediately:

image

Maybe we can suppress it when running from the debugger? Or at least until the final line?

@jpelay
Copy link
Member Author

jpelay commented Oct 29, 2023

This seems to work GREAT from the front-end @jpelay!! Finally a debugger for if's and loops!!!!

One tiny UX issue I saw is that if you start the debugger, you get the confetti immediately:

image Maybe we can suppress it when running from the debugger? Or at least until the final line?

Yes, of course!

@Felienne
Copy link
Member

Wooohoooooo!!! All seems to be in order! I will deploy now, and then merge, so we can test for a bit in alpha just to be sure.

@@ -18,6 +18,7 @@ import { HedyEditor, EditorType } from './editor';
import { HedyAceEditorCreator } from './ace-editor';
import { stopDebug } from "./debugging";

export let theGlobalDebugger: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense (not in this PR but in the future!) to also make an interface for the debugger like we do for the editor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I'm planning on two things: leave the debugger in JavaScript and write an vendor interface like we do for some other Skulpt code we execute . Or, convert it entilery to TypeScript.

The interface we use for Skulpt is here ((

declare const Sk: {
pre: string;
TurtleGraphics?: {
target?: string;
width?: number;
height?: number;
worldWidth?: number;
worldHeight?: number;
};
execStart: date;
execLimit: number;
globals: Record<string, Variable>;
main_canvas: HTMLCanvasElement;
builtin;
abstr;
builtinFiles?: {
files: Record<string, string>;
};
python3: any;
configure(options: any): void;
misceval: {
asyncToPromise<A>(fn: () => Suspension, handler?: Record<string, () => void>): Promise<A>;
promiseToSuspension;
Suspension: { }
},
importMainWithBody(name: string, dumpJS: boolean, body: string, canSuspend: boolean): Suspension;
setTimeout?: (func: () => void, delay: number) => any;
insertPyGameEvent: (eventName: string) => null;
bindPygameListeners(); void;
unbindPygameListeners(): void;
}
))

Copy link
Member

@Felienne Felienne Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a new version of Skulpt (if so, which one) or is this the one we were already supporting (1.3.0 I think? https://github.com/hedyorg/hedy/pull/753/files)? Would be good to document which version we are on, just in case we ever need to update. Because now it is a bit hard to see which it is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a version of Skulpt I built from their source. I also recall we updated that Skulpt version to a more recent one to be able to use Unicode variable names! We did that in this PR: https://github.com/hedyorg/hedy/pull/3119/files

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still need to add the fork to our organization, and include that in the documentation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a version of Skulpt I built from their source. I also recall we updated that Skulpt version to a more recent one to be able to use Unicode variable names! We did that in this PR: https://github.com/hedyorg/hedy/pull/3119/files

Ah yeah, you are right, that is even more recent. We do want to keep that version haha, or we will have to hash all variable names again!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still need to add the fork to our organization, and include that in the documentation.

I guess we want to do that in a next PR? Shall we make an issue so we don't forget?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #4685

outputDiv.append(variableButton);
outputDiv.append(variables);

const storage = window.localStorage;
let skulptExternalLibraries:{[index: string]:any} = {
'./extensions.js': {
path: theStaticRoot + "/vendor/skulpt-stdlib-extensions.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In light of our new changes, might it be safer to load all Skuplt recourses ourselves? I worry a bit that they might update stuff that breaks our new code (but I can't oversee whether that is a reasonable worry, what do you think @jpelay?)

We added this CDN link in #4490 for performance reasons (so we will make @rix0rrr sad haha)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, Felienne, but I don't fully understand what you mean by load all Skulpt resources ourselves? I didn't remove that part of the code, the storage = window.localStorage was being used to know if we were in debug mode, no need to do that since now we have two clearly differentiated execution paths!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah never mind, my bad! I thought thestaticroot was referring to a generic CDN hosting libraries (something like https://developers.google.com/speed/libraries) but I see now it just refers to our own repo, so we can leave it as is!

Copy link
Member

@Felienne Felienne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's gooooooooo!!!!

@mergify
Copy link
Contributor

mergify bot commented Oct 30, 2023

Thank you for contributing! Your pull request is now going on the merge train (choo choo! Do not click update from main anymore, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot merged commit 744b1ba into hedyorg:main Oct 30, 2023
9 checks passed
mergify bot pushed a commit that referenced this pull request Oct 30, 2023
…ests (#4686)

After merging #4465 some Cypress tests were failing. This was because an ad-hoc calcutation I did to figure out the current debugging line. It worked fine locally and even in the tests in the PR, but for some reason failed later on, this PR deabilates that check and I will work on it later this week.

Also, after testing in Alpha I found out I wasn't showing the error messages when an error ocurred during execution. 

How to test?
Go to level 3 and write this code:
```
l is 1, 2
print l at 3
```
Then debug it and when you reach the second line this error should be shown:

![image](https://github.com/hedyorg/hedy/assets/45865185/62836f09-e3e9-403b-bdb0-b99394bd718b)
mergify bot pushed a commit that referenced this pull request Oct 31, 2023
**Description**
Whoops, #4465 broke the embedded editor, this PR adds the required argument to the `runit()` function
@Felienne Felienne added the newsletter PRs to talk about for the CM label Nov 3, 2023
mergify bot pushed a commit that referenced this pull request Nov 17, 2023
**Description**

On #4465 we added a debugger to Hedy that needed to use some changes on the Skulpt library. For that I added a Skulpt version that I took from my own fork; now that we have our fork of Skulpt https://github.com/hedyorg/skulpt I'll use this oportunity to delete some `console.log`s that I left there and also explicitly leave a trace to point to a single PR that changed the version of Skulpt we are using.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
newsletter PRs to talk about for the CM
Projects
Development

Successfully merging this pull request may close these issues.

[LANGUAGE idea] Integrate Skulpt debugging capabilities into Hedy
3 participants