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]

  • I think the Generate Optimized Project stopped working

    You're right. Build/Build and Run/Export all still work though...

  • What SDK are you using? I use the latest 4.1.3 and I have actually the following in my code:

            var result as String;
            if (confFormat != null) {
                var intHr = computeIntHR(info) as String;
                result = Lang.format(confFormat, [intHr, extHr]);
            } else {
                result = extHr;
            }
            return result;
    

    And i tried now that even with my type it works:

    var x as ExtHRMFitContributor;
    Yes I have using Toybox.WatchUi as Ui in another file.

    But I tested: when I added 
    using Toybox.WatchUi; to the SAME file, then it works.
     
  • v2.0.12 is out which fixes that, the super-class name injection issue, and some type lookup issues.

  • What SDK are you using?

    I'm using 4.1.3 too, and I get the same result in all recent releases.

    The only way I can get your code to compile is to turn off type checking... are you sure you've got it enabled? As a datapoint, in a previous post you included:

    > Executing task: java -Xms1g -Dfile.encoding=UTF-8 -Dapple.awt.UIElement=true -jar /home/flocsy/.Garmin/ConnectIQ/Sdks/connectiq-sdk-lin-4.1.3-2022-05-10-b3aaee541/bin/monkeybrains.jar -o bin/X.prg -f /home/flocsy/garmin/X/monkey.jungle;/home/flocsy/garmin/X/barrels.jungle -y /home/flocsy/garmin/developer_key.der -d fenix6_sim -w <

    which doesn't have a `-l` option, so has type checking disabled.

    The documentation isn't 100% clear, but certainly implies that locals can't be typed:

    Unlike with class instance variables, the Monkey Types system will infer the type of a local variable by tracking assignments

    and none of the examples put a type on a local declaration. Also the error message I posted says pretty unambiguously that local types can *only* be inferred, not declared.

    But I tested: when I added using Toybox.WatchUi; to the SAME file, then it works

    Ok, there is something strange with monkeyc's behavior here. I tried removing *every* import/using of Toybox.WatchUi in my main project. Initially the typechecker caused the build to fail, but I turned it off, and the build succeeded, and my project ran. But then I tried the SimpleDataField sample project, and even with the typechecker off, the build fails without at least one import/using. But then I realized that some resources - eg menus - must pull in WatchUi, and that does seem to be whats going on. 

    [Edit: I took a look at bin/internal_mir/Rez.mir, and although it *does* import various Toybox modules, the imports all happen inside the relevant Rez modules. So while Rez.Menus.WatchUI should be available in projects with menu resources, I wouldn't expect $.WatchUi to be available. So I don't think the resource theory is correct. But *something* seems to pull in WatchUi in some projects, but not others]

    So without analyzing all the resources (and I'm not even sure how to figure out in a general way which resources might pull in which modules, so I'd have to just come up with an ad hoc list of rules), I can't tell whether WatchUi (or possibly other modules) was pulled in or not.

    So I think I'm going to stick with the current behavior. If you have a using/import anywhere in the .mc files, that should work for all files. And I've tested, and it does. The reason your case doesn't work is that you have using Toybox.WatchUi as Ui which only makes the name Ui available in all files, not WatchUi (and I confirmed via SimpleDataField, that putting a using as in one file, or all files, did not fix the errors - although if I renamed every WatchUI to Ui, then it did fix them).

  • You are right I did not have the typecheck on.

  • You can't use type checking if you are using "using".  You need to use "import"

    Type checking is strictly a compile (actually pre-compile) thing and there's no runtime checking involved.

    Personably, I find including Monkey Type stuff in the code where you're not doing type checking ( no -l1,-l2, -l3) makes the code a bit "messy" with no value add in my opinion

  • You can definitely use typechecking with using, its just a little more verbose.

    If you use "import Toybox.Lang;", you can then say "var foo as Number". If you use "using Toybox.Lang;" you have to say "var foo as Lang.Number" instead. But note that the latter works for both using and import. Basically import just pulls all top level type declarations from the imported module directly into the scope of the import, while using only pulls in the module itself.

    Also, as discussed above, neither using, nor import needs to be in the file that refers to the used module. So a single "import" anywhere in the project takes care of importing that module into every .mc file, even ones that have their own "using" statement.

    I'm actually in two minds about "import" because it can lead to some confusing cases... is it "new Menu2()" or "new WatchUi.Menu2()"; "x instanceof Menu2" or "x instanceof WatchUi.Menu2" (on the other hand, I use import exclusively in my projects, because thats what garmin recommends, and by the time I started noticing the issues I'd written a lot of code).

    I was also initially worried about namespace pollution from import, since it pulls in so much. But it turns out not to be an issue, because monkeyc keeps separate lookup tables for types and values. So that

    typedef Foo as Number;

    module Bar {

      var Foo as String = "Foo";

      function bar(x as Foo /* x is a Number */ ) as String {

        return Foo; // returns "Foo"

      }

    }

    actually compiles. In fact, you can drop module Bar, and put both Foo declarations in the same module, and it still works...

  • I should have said using "using as".   Something like 

    using Toybox.WatchUi as Ui;

  • I should have said using "using as".

    Still works, you just have to say "as Ui.View", rather than "as WatchUi.View" (or just "as View" if you had used "import").

    That said, I would strongly recommend against "using as" in any circumstances. Garmin seems to have decided it was a mistake.