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 I can know if the build is with or without Prettier Optimizer?

    I think so, but it's a little awkward. You'd have to add "when_with_prettier" to your base.excludeAnnotations in your monkey.jungle. Then we'd need an option in the optimizer to add and remove excludeAnnotations; in this case you'd want to add "when_without_prettier" and remove "when_with_prettier".

    Or we could drop the option and just say that those are the names, and the optimizer always removes "when_with_prettier" and adds "when_without_prettier" (if we go that route, I'd probably pick different names).

    But I think the option could be generally useful. We could have an "extraExcludes" option, which could be overridden in build and launch configs, and you would set something like "extraExcludes: foo, -bar", which would add foo and remove bar. Then in addition to making this feature possible, you could have multiple build configs that build different variants of your app.

  • Let's start with the "built-in" that doesn't need configuration (you can add the more complex things later :) 

    I agree a better name would be welcome.... Maybe one f these pairs:

    optimizer, no_optimizer

    optimized, not_optimized

  • Application.loadResource(Rez.JsonData.label_data_1);

    Yes, sorry, my fix was pretty much entirely wrong.

    It was supposed to check whether Toybox.Lang.ResourceId existed, and use that if so, or Toybox.Lang.Symbol otherwise. But the check was incorrect and always said it existed, so it always tried to use ResourceId, which wasn't defined under 6.x, and got interpreted as Any. But my tests were (accidentally) set to TypeCheckLevel=Gradual, which meant that "Any" was acceptable as a parameter, and no error/warning was produced.

    Anyway, a new fix should be going out soon.

  • Fix is out: v2.0.77 (may take a few minutes to show up)

  • v2.0.78 is out, and adds an extraExcludes option, and more Toybox documentation on hover.

  • I noticed something that in contra intuitive. Do you know how to explain it?

    function is_numeric(str as String) as Boolean {
        var chars = str.toCharArray(); // api 1.3.0
        var result = true;
        for (var i = chars.size() - 1; i >= 0; i--) {
            // if (NUMERIC_CHARS.find(chars[i].toString()) == null) {
            if (NUMERIC_CHARS.find("" + chars[i]) == null) { // -3 bytes
                result = false;
                break; // +3
            }
        }
        return result;
    }
    

    How come that c.toString() is 3 bytes longer than "" + c where c is a char?
    I would think that "" + c probably implicitly calls toString anywhere and it also includes an concatenation so I would expect it to be longer code than to just call toString implicitly.

  • pmc-analysis bug:

    function foo(c as Char) {
        return '0' <= c && c <= '9';
    }

    Gives a warning: "Unexpected types for operator '<=': [Char vs Char]"

  • I'll take a look...

  • I think it's because the conversion really is implicit (I think you meant that the compiler would insert a call to toString, but it doesn't). Assuming the char is in a local "c", the byte code is just

     

        spush "" // 5 bytes, plus storage for string
        lgetv c  // 2 bytes
        addv     // 1 byte

    While a call is pretty complex:

        lgetv c         // 2 bytes
        spush toString  // 5 bytes
        getv            // 1 byte
        frpush          // 1 byte
        invokem 1       // 2 bytes

    Note that identical strings are deduped, so if you mention "" anywhere in your program, there's no extra overhead for mentioning it here. I've not really looked into how much space a string takes up, but I would guess its 4 bytes plus the string data.

  • import Toybox.Lang;
    import Toybox.WatchUi;
    
    class ResourceCache {
        hidden var cache as Dictionary<Symbol, WeakReference> = {} as Dictionary<Symbol, WeakReference>;
    
        function get(symbol as Symbol) as Object? {
            var cache = self.cache;
            var value;
            var weakReference = cache.get(symbol);
            if (weakReference == null || !weakReference.stillAlive()) {
                value = WatchUi.loadResource(symbol);
                cache.put(symbol, value.weak());
            } else {
                value = weakReference.get();
            }
            return value;
        }
    }
    

    Bug on the line with "weekReference == null":

    This comparison seems redundant because weakReference should never be null [pmc-analysis]

    I think the bug is that the value type of the cache Dictionary is indeed WeakReference (and there's no "or Null"), but the Dictionary.get does return null if the key is not in the dictionary.