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]

  • ERROR: Internal: Error: Comment ",intZone3" was not printed. Please report this error!
    Error: Comment ",intZone" was not printed. Please report this error!
            at Object.ensureAllCommentsPrinted (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/prettier/index.js:8136:17)
            at coreFormat (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/prettier/index.js:8682:16)
            at formatWithCursor2 (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/prettier/index.js:8864:18)
            at /home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/prettier/index.js:39174:12
            at Object.format (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/prettier/index.js:39188:12)
            at api_formatAst (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/@markw65/monkeyc-optimizer/build/api.cjs:2521:46)
            at /home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:13099:74
            at async Promise.all (index 4)
            at async Promise.all (index 3)
            at async generateOptimizedProject (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:12984:5)
            at async buildOptimizedProject (/home/gavriel.fleischer/.vscode/extensions/markw65.prettier-extension-monkeyc-2.0.20/node_modules/@markw65/monkeyc-optimizer/build/optimizer.cjs:12735:61)


    It's inside a regular function annotated with (:memory32K) and obviously only one of them is active during compilation:

        (:memory16K)
        public function compute(info as Activity.Info) as Lang.Numeric or Time.Duration or Lang.String or Null {
            var extHr = computeExtHR();
            if (mTicker > FOUND_SECONDS) {
                extHr = strfHrZone(mConfFormat, extHr, "", computeIntHR(info) /*,intZone2*/);
            }
            return extHr;
        }
        (:memory32K)
        public function compute(info as Activity.Info) as Void {
            var extHr = computeExtHR();
            var extZone = self.mHeartRateZone;
            if (extZone == -1) {
                extZone = "-";
            }
            if (mTicker > FOUND_SECONDS) {
                extHr = strfHrZone(mConfFormat, extHr, extZone, computeIntHR(info) /*,intZone3*/);
            }
            mResult = extHr as String;
        }
    


    Strangely, when I optimize for fenix3 (which has 16K memory) then it works ,but when I optimize for fenix6 with 32K memory then it fails.

  • It's inside a regular function annotated with

    I can't repro with your example... is computeIntHR getting inlined by any chance?

  • yes

        (:release, :inline)
        hidden function computeIntHR(info as Activity.Info) as AlphaNumeric {
            return (info != null && info.currentHeartRate != null)
                ? info.currentHeartRate
                : NO_VALUE;
        }
        (:debug, :inline)
        hidden function computeIntHR(info as Activity.Info) as AlphaNumeric {
            return ((info != null /*&& info has :currentHeartRate*/ && info.currentHeartRate != null)
                ? info.currentHeartRate
                : (MOCK ? INT_HR_MOCK : NO_VALUE)) as AlphaNumeric;
        }
    

  • "Nobody should be using this tool because nobody is using it."

    The same argument could be made about Monkey C and Connect IQ itself. (The Monkey C plugin has a whopping 25,000 downloads in the Visual Studio Code marketplace)

    Personally I think this is a great idea (although I don't use it myself because I don't have time for Connect IQ development at the moment, and TBH idk if I'll ever feel like returning to it).

    To me it makes sense that the prettier function would change files in place, but the optimizer function would place modified source files in an output folder. Not too different in principle than transpilers for typescript and javascript - in this case, the input is standard Monkey C, and the output is optimized Monkey C. Usually when you deal with code to be transpiled, you want to work on the original as it's easy to read and maintain. If you work on the output, that sort of defeats the purpose of transpilation.

    My main concern would be that this is an unsupported tool which wouldn't necessarily be guaranteed to produce correct code which behaves 1:1 with the original source. This is where you'd really need automated testing to come in to play, which also doesn't seem to be a strong suit of the CIQ dev ecosystem.

    I also think it's natural that devs would prefer to use things of this nature that are officially supported by Garmin, as opposed to 3rd party tools. For example, most people would probably feel a lot better about using the official optimization support that comes with Garmin.

    Beyond that, there's certain kinds of manual optimization that can probably never be done in automated fashion (like bit-packing of data). Doesn't mean that this isn't a really nice tool for anyone who's willing to use it.

    Even if you don't use the optimization function, the prettier and the code navigation features sound useful, assuming they're well-implemented. Lack of both of those things is a big feature gap in Monkey C, IMO. Sure, you can use "beautify (javascript)" on Monkey C, but it doesn't look great in 100% of the cases. And people have been asking for a Garmin-supported code beautifier for quite some time.

    From a personal POV, in my day job I work with both mature, standard languages with fully featured beautifiers/linters and code navigation, and a custom language that has neither of those things. The dev experience is like night and day. Sure, I can do both, but I know which way I prefer -- the way that has less friction and requires me to do much less manual housekeeping.

    I think the reason we don't hear from the "community" on certain feature requests is because, ironically, they make these requests early on and or several times over the years, and are often ignored. (Which is fine, I get that Garmin has a limited amount of resources.) This can be seen with the number of feature requests that come up with newer devs which were originally requested like 5-7 years ago.

  • (hidden away in the bin directory)

    actually once you have created your optimized code you're not able to run non optimized code because by default the source path search location is base.sourcePath = .\**.mc so it will find the "optimized" version before it finds the real source file.

    you can use all three independently

    how do you disable prettier?

  • how do you disable prettier
    • You can disable the Prettier extension, either globally, or on a per-project basis.
    • You can configure default formatters per language
    • You can simply choose not to format your code. VScode doesn't auto format, unless you've specifically configured format-on-save, or format-on-type.

    actually once you have created your optimized code you're not able to run non optimized code

    I agree thats a bit annoying, but it's certainly not the case that "you're not able to". The simple fix is to just put "base.sourcePath = source" (or whatever is needed to cover your actual source files) at the top of your monkey.jungle. I've been thinking I should probably add a notification about that issue, with an offer to auto-fix it. You can also set the prettiermonkeyc.outputPath setting to somewhere outside your project, if you really don't want to change your .jungle file.

  • Hmm. Still no luck even with computeIntHR getting inlined. Staring at the code, and digging through Prettier's default comment handling code, there does seem to be a potential problem with trailing comments on type attributes - which is what you've got here. After inlining it ends up as "(expression) as AlphaNumeric /* ,intZone3 */).

    But even then, the default comment handler kicks in unless its the child node of a call (which is true here - its a call argument) AND the comment looks like a "Flow Annotation". I'm not even sure what that is, but ultimately its a comment beginning with ::. And in fact, I *can* repro your problem if I change the comment to /* ::intZone3 */.

    In fact, all it takes is "foo(4 as Number /* ::foo */)", and the problem repros. But I can't see how to make it fail without the "::" comment.

  • Maybe you need this as well:

        (:inline)
        hidden function strfHrZone(format as String, extHr as AlphaNumeric, extZone as AlphaNumeric, intHr as AlphaNumeric /*,intZone as AlphaNumeric*/) as String {
            var name = mName != null ? mName : "";
            log("strf: " + format + ", " + extHr + ", " + intHr + ", " + name + ", " + extZone);
            return Lang.format(format, [extHr, intHr, name, extZone /*,intZone4*/]);
        }
    

  • Maybe you need this as well:

    Yes, that did it. Just need to debug now...

  • v2.0.22 should fix the issue.