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]

  • When substituting numbers for color values, hex codes would be easier to interpret

    I've had requests like this before, and I was going to give the same answer - at the point of the substitution, I don't have the context to do this. But this actually is a special case where its probably easy...

  • v2.0.73 is out. I skipped 2.0.72 because of some last minute nits.

    Highlights:

    • The type checker now reports issues with operator operands - as Garmin's already did - but does it in real time, as you type.
    • The type checker now tracks object literal types (things like { :foo as String }). Again, this is similar to what Garmin's type checker already does, but in real time, and hopefully more accurately. This should fix the issues reported by in this thread
    • Speedups for the monkeyc parser, the jungle parser and the resource parser.
  • 0xaa00ff as Toybox.Graphics.ColorValue

    Sweet. Thanks.

    Only one thing left for me, the unfixable? Rez.Drawables as Dictionary kludge.

    Regarding arrays, the optimizer adds a local variable to copy a class array. Does this duplicate the array + contents, or just create a duplicate of the object id so it reduces the lookup time? I have a function that references (reads, no writes) three 38 element one-dimensional arrays from a class. This function is called about 20 times from yet another function's loop. I'm trying to figure out the best way to manage this space and time wise.

  • Only one thing left for me, the unfixable? Rez.Drawables as Dictionary kludge

    Is this the thing where you want to do something like

    function handleDrawable(drawable as Symbol) {
      ...
      var d = Rez.Drawables[drawable];
      ...
    }

    And my type checker is happy, but Garmin's type checker complains that it can't determine the container type, so you change it to

    function handleDrawable(drawable as Symbol) {
      ...
      var d = (Rez.Drawables as Dictionary)[drawable];
      ...
    }

    and now my optimizer complains that you're pretending that something that can't possibly be a Dictionary is a Dictionary?

    So now you have to make it

    function handleDrawable(drawable as Symbol) {
      ...
      var d = (Rez.Drawables as Object? as Dictionary)[drawable];
      ...
    }

    and both type checkers are happy - but its kind of ugly...

    My solution for this is to just pull out the getter as

    (:typecheck(false))
    function getDrawable(drawable as Symbol) as Symbol {
      // you could maybe include the loadResource here too
      // (assuming its that kind of Drawable).
      return Rez.Drawables[drawable];
    }

    You can add :inline too if you'd rather not have the call overhead.

  • I didn't mean to imply that I need it fixed, I'm OK with the kludge I have knowing that it would be a Herculean effort to fix properly (if there is such a thing). What I have been able to do is see what the optimizer does and apply some of that to the extent that the code is still maintainable. Doing so I have been able to shave the code for my wf down to under 22k which is a lot smaller and faster than it was not too long ago. Thanks again for your work on this, it's really helpful.

  • I have a question / concern. I have some code like this:

    var p = (someVal > 0) ? 1 : -1;
    for (var a = 0; a <= 3.142; a += const_PI_12) {
        var r = sinPhase * Math.cos(a);
        myFunc(r, a * p);
    }
    

    I have the definition of p in an if statement before the loop because it doesn't change, but the optimizer moves it into the loop because p is only used once. This seems like the opposite of an optimization. How can we tell the optimizer not to move code that doesn't change into a loop, or conversely to leave code that doesn't change out of a loop?

  • Personally, as a developer for 40+ years, I would NEVER use something that modifies my code other than basic pretty-printing (line up tabs, brackets, etc so things are more readable)

    If it finds something that it thinks might be done better, it can notify me as the dev, but let me decide what's best for the app and make the change myself.

  • This seems like the opposite of an optimization

    Kind of. I'm a bit surprised it did this, but it *is* a size optimization, which is what my optimizer is aimed at. It should save 2 bytes for the assignment to p, and 2 bytes for the read from p.

  • Personally, as a developer for 40+ years, I would NEVER use something that modifies my code

    It doesn't modify your code. It makes an optimized copy of your code, and builds that. Its really no different from any of the compilers you've ever used; it just happens that instead of compiling from source to object code, it (by necessity) compiles from source to another version of the source; and then uses garmin's compiler to compile that. But you never have to see or care about the optimized copy in any way - unless of course you want to (or you think it's buggy).

    And since my optimizer creates the optimized copy in about a 10th the time it takes for gamins compiler to compile, you don't really notice the extra compile time. I could actually change it so that it creates a bunch of temporary files, compiles those, and deletes them. Then you wouldn't have to worry about it "changing your code".

  • I'm not really concerned about compile time, that will happen once. Some loops like figuring out clipping for the second hand run 3600 times an hour. This particular optimization is for space at the expense of speed/efficiency. I don't think the space gain is worth the extra cycles especially when they are being counted. I posted a simplified version of the code for clarity, the actual code is more complex so the hit is actually worse. It's a fundamental rule of programming to move code that always gives the same result independent of the loop and just do it once before entering the loop. Maybe I'm alone on this but moving code from before a loop into a loop is just not right. What are the options to keep this from happening?