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]

  • Sounds reasonable to only propagate typecheck if parent hasn't got one yet. Maybe there could also be a warning when you can't propagate (especially that it's possible that parent doesn't have it in the original code, but it got one because of a previous inclined function) with the exception when not propagating it shouldn't matter (not sure if this is something you can know though, but the obvious example is if the same typecheck annotation is the one already present on the parent)

  • So thinking about this, there's a simple solution.

    If either function has a typecheck(false), I should add typecheck(false) to the caller, and drop any other typecheck annotation. Otherwise, make a list of all the distinct names that appear (currently I think the only valid ones are disableBackgroundCheck and disableGlanceCheck - but I can just gather whatever is there), and combine them into a single typecheck([...names]). If there's only one name, I can drop the square brackets.

    I think that should handle everything...

  • There's a new bug in 2.0.48:

    import Toybox.Application;
    import Toybox.Lang;
    import Toybox.Math;
    
    (:api2, :inline)
    function getImpl(key as PropertyKeyType) as PropertyValueType or Null {
        return Properties.getValue(key);
    }
    function getConf(key as PropertyKeyType) as PropertyValueType or Null {
        var val;
        try {
            val = getImpl(key);
        } catch (e) {
            val = null;
        }
        return val;
    }
    
    
    (:inline)
    function getNumber(key as PropertyKeyType, defaultValue as Number) as Number {
        return toNum(getConf(key), defaultValue);
    }
    function toNum(value as PropertyValueType?, defaultValue as Number) as Number { // code: 10396 data: 4219
        if (value instanceof Lang.Boolean) {
            return value ? 1 : 0;
        }
        value = value != null && value has :toNumber ? value.toNumber() : null;
        return value != null ? value as Number : defaultValue;
    }
    
    public function uninitializedBug() as Void {
        var zero = 0;
        var u = "u";    var antId = Math.rand();
        var emptyIndex = null;
        for (var idx = 1; idx <= 2; idx++) {
            var extAntId = getNumber("X" + idx, zero);
            if (antId == extAntId) {
                emptyIndex = idx;
                break;
            } else if (emptyIndex == null && extAntId == zero) {
                emptyIndex = idx;
            }
        }
    }
    

    In the generated code of the last function you have:

        var extAntId;
        if (antId == toNum(getConf("X" + idx), 0)) {
          break;
        } else if (emptyIndex == null && extAntId == 0) {
          emptyIndex = idx;
        }
    

    so I get:

    bin/optimized/group005-debug/source/source/Test.mc:30,11: Value may not be initialized.

  • There's a new bug in 2.0.48

    I can repro. Turning off "Single Use Copy Prop" fixes it.

  • ERROR: Internal: Error: Comment "class Stats { // comment" was not printed. Please report this error!
    Error: Comment "class Stats { // code: 11622 data: 4974" was not printed. Please report this error!
    at Object.ensureAllCommentsPrinted (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/prettier/index.js:8109:17)
    at coreFormat (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/prettier/index.js:8655:16)
    at formatWithCursor2 (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/prettier/index.js:8837:18)
    at /Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/prettier/index.js:37229:12
    at Object.format (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/prettier/index.js:37243:12)
    at formatAst (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/@markw65/monkeyc-optimizer/build/api.cjs:13981:46)
    at /Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:16662:78
    at async Promise.all (index 18)
    at async Promise.all (index 5)
    at async generateOptimizedProject (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:16522:5)
    at async buildOptimizedProject (/Users/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.48/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:16220:61)

    my code looks like:

    (:background_app, /*:api1_3,*/ :stats) // comment
    // class Stats { // comment
    module Stats { //
    I am in the middle of switching this from being a class to a module
    I'll add that I'm really in the middle, so it doesn't compile without the optimizer either, but I thought this might interest you anyway
  • It's a formatter bug. Your example, with a closing brace added, crashes the formatter...

    (:background_app, /*:api1_3,*/ :stats) // comment
    // class Stats { // comment
    module Stats { //
    }

  • There's a new bug in 2.0.48:

    v2.0.49 fixes that, and the formatter crash.

    It also extends single use copy prop to work with update assignments, so that

    var x = a + b; x += c; becomes var x; x = (a + b) + c; and

    ...; x += c; return x; becomes ...; return x + c;

  • Ok, 2.0.49 works.

    There was (for a some time) a warning that some of my code will only compile with the newer compiler or -O1+. For example:

    import Toybox.Application;
    Properties.setValue(key, val);
    So until now I added Application to get rid of the warning:
    Application.Properties.setValue(key, val);
    but now I realized that that adds 6 bytes to the code, and instead there seems to be a better solution:
    import Toybox.Application;
    import Toybox.Application.Properties;
    Properties.setValue(key, val);
    I don't know if this was known to you, or if it's intentional or not, but looks like another tiny optimization opportunity.
  • I don't know if this was known to you

    Yes, I've been aware of it for a long time, but I've not implemented the optimization. For one thing, there aren't many opportunities... I think it's only Properties, Storage, and Gregorian.

    I suppose if someone wrote Toybox.Application without doing the import I could optimize that - but I don't really see any examples of that (except in some of my tests!). In fact I think people generally assume (as I did for a long time) that you *have* to import Toybox.Application if you want to use Application - when in fact it just saves using the full path.

  • import Toybox.Lang;
    import Toybox.System;
    
    (:foo)
    function notHere() {
        System.println("notHere");
    }
    
    (:foo)
    function cond() as Boolean {
        return true;
    }
    (:bar)
    function cond() as Boolean {
        return false;
    }
    
    (:bar)
    function b() {
        if (cond()) {
            notHere();
        }
    }
    

    When you compile this with excludeAnnotation foo then there's a warning:

    ERROR> fenix6: Test.mc:21:9: 'notHere' is not callable

    Which is kind of true, but the optimizer later is more clever and removes the if (cond()) block, because it understands that cond() will always be false, so basically the warning was not needed.

    This is not a huge deal, just that you know about it.