Big update to prettier-extension-monkeyc

I've posted about prettier-extension-monkeyc before, but I've added a bunch of new features that developers will probably like (well, I've been missing them, so maybe you have too).

The new features it implements for VSCode include:

  • Goto Definition. Point at a symbol, Ctrl/Cmd click, and it will take you to the definition. Or F12
  • Goto References. Right click on a symbol and select "Goto References". It will show you all the references. Or Shift-F12
  • Peek Definition/Peek References. Same as above, but in a popup window so you don't lose your place in the original document.
  • Rename Symbol. Right click on a local, function, class or module name, and select "Rename Symbol". It will rename all the references. It doesn't yet work for class members/methods.
  • Goto Symbol. Type Ctrl/Cmd-Shift-O and pick a symbol from the drop down (which has a hierarchical view of all symbols in the current file). This also appears as an outline across the top of the file.
  • Open Symbol By Name. Type Ctrl/Cmd-T, then start typing letters from a symbol name. A drop down will be populated with all matching symbols from anywhere in your project.

Older features include a prettier based formatter for monkeyc, and a monkeyc optimizer that will build/run/export an optimized version of your project.

[edit: My last couple of replies seem to have just disappeared, and the whole conversation seems to be in a jumbled order, so tldr: there's a new test-release at https://github.com/markw65/prettier-extension-monkeyc/releases/tag/v2.0.9 which seems to work for me on linux. I'll do more verification tomorrow, and push a proper update to the vscode store once I'm sure everything is working]

  • Is there a way to use different type check levels for pmc-build and pmc-analysis

    Not using the "Build Optimized Project" and related build commands. But you could set the project scope typeCheckLevel to whatever you want for analysis, and then create tasks, or launch configs for your builds, which specify the typeCheckLevel explicitly. Something like this in .vscode/tasks.json:

    {
      "version": "2.0.0",
      "tasks": [
        {
          "type": "omonkeyc",
          "device": "fenix5xplus",
          "simulatorBuild": true,
          "label": "Optimized: fenix5xplus",
          "typeCheckLevel": "Strict",
          // other options...
          "group": {
            "kind": "build",
            "isDefault": true
          }
        }
      ]
    }

    If you set "kind": "build", vscode knows it's a build command, and Cmd-Shift-B will pop up a menu of all build commands to choose from; if you also set "isDefault":true, then Cmd-Shift-B will always run that specific build command when you hit Cmd-Shift-B.

    If you want to build and run, you can put something similar in launch.json.

  • I see warnings by live code analysis for slightly different case

    Older versions of garmin's compiler only supported literals for enum values, so that's what I originally implemented. I only hacked in support for general expressions quite recently as a result of a bug report. Currently getting this right relies on the optimizer substituting the value of CONST into the enum for type analysis to get the correct result. But the optimizer doesn't run in analysis mode.

    While I could almost certainly fix this one with another small hack, I have a rewrite of the analysis phase planned that should take care of this; but its probably going to be a while before I get around to it.

  • I have a strange error "Maximum call stack size exceeded [pmc-analysis]" when I open one of my projects, but I can't create a simple test to reproduce this.

    I cloned prettier-extension-monkeyc, prettier-plugin-monkeyc and monkeyc-optimizer repositories and launched VS Code in extension development mode to debug plugin itself. This error was caught in "project-manager.ts" in "runAnalysis". I added "console.log(e);" to catch clause and got this:

    RangeError: Maximum call stack size exceeded
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16656:49
    	at Array.forEach (<anonymous>)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16656:41
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>)
    	at markOverrides (.../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16655:10)
    	at .../prettier-extension-monkeyc/node_modules/@markw65/monkeyc-optimizer/build/chunk-2OEXWI65.cjs:16664:9
    	at Array.forEach (<anonymous>) {stack: 'RangeError: Maximum call stack size exceeded
    …5.cjs:16664:9
    	at Array.forEach (<anonymous>)', message: 'Maximum call stack size exceeded'}
    
    

    Is there a way to debug original optimizer's source code files instead of built chunks to find the exact stack trace where problem occurs? I have almost zero experience with JS/TS.

    And is there anything else I could do on my side to give you a hint what exactly goes wrong?

  • Is there a way to debug original optimizer's source code files instead of built chunks

    See below, because I'm pretty sure I know what's going on, and how to fix it. But for future reference, or if you're just curious, there are a couple of ways.

    If you need to debug the optimizer as part of the extension, run the "mc-live" script from prettier-extension-monkeyc. It creates a new commit, and will refuse to run if there are any modified files (so commit or stash them first). If you're not familiar with npm scripts, you can run it from a command prompt in the prettier-extension-monkeyc directory, via "npm run mc-live", or in vscode you can find it in the npm-scripts tab in the side panel, and hit the run button over to the right.

    Once you've done that, you can set breakpoints in, and step through the monkeyc-optimizer source while running the extension. Also, any changes you make to the monkeyc-optimizer will be picked up by the extension the next time you run it (ie you don't need to re-run the mc-live command to pick up changes). You do need the watch-scripts to be running (but they should start automatically).

    But it's usually easier to just run the code directly in the monkeyc-optimizer. To do that, open a javascript debug terminal in the monkeyc-optimizer project (Cmd-Shift-P Debug: JavaScript Debug Terminal), and then try:

    % node test/test.js --analyze-only --jungle path-to-my/monkey.jungle

    That should run the same analysis pass as the extension, and again, stop at any breakpoints you set in the code.

    And is there anything else I could do on my side to give you a hint what exactly goes wrong?

    The backtrace tells me exactly what's going wrong, and I know how to fix it - but I'm curious as to what's going on. If you disable the plugin, does your project build and run?

    The code that's failing is marking all the class methods that get overridden (so that when I see a call to a class method, I know whether its definitely to this method, or if it might go to an override), and it's doing so by blindly walking the class hierarchy. So there must be a cycle in your class graph. something like A extends B, B extends C, C extends A. I remember thinking that I ought to check for that, but deciding I didn't need to because correct code would never do it.

    But I guess you could actually have correct (but very odd) code that caused this. Something like:

    (:foo)
    class B {}
    (:foo)
    class A extends B {}
    (:bar)
    class A {}
    (:bar)
    class B extends A {}

    When analysis is run, excludeAnnotations are ignored, and every variant of your code is analyzed at the same time. So the analyzer sees both A extends B, and B extends A, and goes into infinite recursion.

    So I'm guessing that you either have a bug, or some very odd code :-)

  • node test/test.js --analyze-only --jungle path-to-my/monkey.jungle

    Great! I can debug (though I don't fully understand what exactly I'm debugging Smile), I see this error in stdout, and its trace is much shorter:

    Debugger attached.
    While building '.../monkey.jungle
    RangeError: Maximum call stack size exceeded
        at .../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17437:49
        at Array.forEach (<anonymous>)
        at .../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17437:41
        at Array.forEach (<anonymous>)
        at markOverrides (.../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17436:10)
        at .../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17445:9
        at Array.forEach (<anonymous>)
        at markOverrides (.../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17436:10)
        at .../monkeyc-optimizer/build/chunk-Z6MS3ANE.cjs:17445:9
        at Array.forEach (<anonymous>)
    Total runtime: 2503ms
    Failed:
    Error: Failed to build '.../monkey.jungle' with error RangeError: Maximum call stack size exceeded
        at driver (.../monkeyc-optimizer/build/driver.cjs:1326:11)
    Waiting for the debugger to disconnect...

    If I get something more, I'll write here.

    If you disable the plugin, does your project build and run?
    some very odd code

    My code can be indeed strange (at least at the moment) as I am doing a major refactoring for extracting 70+ classes from app into a barrel. A large part of the code is already moved, but there's a lot of work to be done. Nevertheless, this project and the barrel right now build and run without errors and warnings with strict type checking both using Garmin's plugin commands and using direct barrelbuild/monkeyc/monkeydo calls. Unit tests and manual testing finish successfully. So the code is ugly, but it is working.

    there must be a cycle in your class graph

    As project builds, I guess there are no such cycles. I have annotations only on class members and not on classes/modules level, so the hierarchies are the same for any excluded annotations.

    One thing I've just noticed is when I add this work-in-progress barrel to project as a reference to a compiled barrel file, everything is fine. If I replace it with a reference to a barrel's jungle file, that was used during barrelbuild call, then I see this error. Build finishes without errors for both variants.

  • As project builds, I guess there are no such cycles

    But as I said, there could be cycles that only exist when you don't apply the excludeAnnotations. So every device builds separately, but the union contains cycles.

  • I just pushed a potential fix to the "infinite-recursion" branch of monkeyc-optimizer. I think it will fix the immediate problem, but whatever is triggering this might cause other, similar problems once we get past that one. Please give it a try, and let me know how it goes.

  • infinite-recursion

    Unfortunately, this fix doesn't help (at least I still see same or very similar errors at the same place - I didn't compare them line by line), and I'm sure I'm using the version with the fix as the compiled chunk from stack trace contains the new code:

      const seen = /* @__PURE__ */ new Set();
      state.allClasses.forEach((elm) => {
        if (seen.has(elm))
          return;
        seen.add(elm);

    However, with the help of debugger I've found the offending class and shrank my code to a simple test case.

    For some reason I cannot attach it to this post directly, so I have to use an external service: https://www.dropbox.com/scl/fi/hgbi5htcrgfyhq3d3sazn/maximum-call-stack-size-exceeded.zip?rlkey=b9dbbtdrd3y3wt53e9ctf6e3d&st=nc7e8l5t&dl=0. I hope, it's OK.

    Don't be surprised to see barrel's source code inside "bin" directory - I use a simple code post-processing, put target files to "bin/gen" and build the barrel using jungle file that has this directory in sourcePath, while the original source stays in "source" in barrel's root.

    P.S. I realized, I must've confused you saying about "bin/gen", as Garmin's compier also uses "bin/gen" for its own purposes. Actually I have multiple folders inside "bin" with generated code, and none of these clash with Garmin's or PMC's folders. I named it "gen" only for this test to indicate, that it's not the place where I store my code, but it's just a step in the build process. The crucial point is that whatever the folder's name is, this problem occurs only when barrel's source is inside "bin".

    P.P.S.

    this problem occurs only when barrel's source is inside "bin"

    This looks like an explaination of what I saw earlier:

    One thing I've just noticed is when I add this work-in-progress barrel to project as a reference to a compiled barrel file, everything is fine. If I replace it with a reference to a barrel's jungle file, that was used during barrelbuild call, then I see this error.
  • Unfortunately, this fix doesn't help

    Yes, sorry, I was doing that in a hurry, and I put the add and has checks in the wrong place.

    I've got some time now, so I'll work out the correct fix, and test it...