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]

  • My issue with that is that now you're writing in a different language

    This also allows you to do things that are impossible in Monkey C (like split up code blocks in a way that would normally be syntactically impossible) - that's probably not a good thing.

    As noted in the previous comment, at least it's explicitly:

    - a compile-time construct

    - a separate "build pass"

    Having said that, there's a reason most modern languages don't support this. The problem with Monkey C is that the environment is so memory-constrained that when you use any halfway "modern" features like dictionaries or classes, you waste a ton of memory.

  • I use that with the prettier monkeyc :)

    I have constants like:
    (:debug) const MOCK = true;

    (:release) const MOCK = false;

    and in some places I use:
    if (MOCK) {do something}

  • wrote: "Even if I build a list of known *initial* firmware releases for every device, there's no guarantee that an api won't be deprecated, and eventually removed"

    That's true, but I think there are a few different types of optimizations:

    - one based on the minimum possible CIQ version on any device (let's assume we'd know it) then based on that you can remove some code that will never be useful on the device.

    - one based on the maximum possible CIQ version. In some cases this can change (pushing the version further up) and I guess in some cases Garmin knows. Well we all know that Epix(1) or FR235 will never get any more update. But of course as it was stated above if that would happen (X-mas miracle) then still nothing would be broken, if some new features are added.

    - the only problem you fear is that deprecated functions will eventually be removed. I understand why you write that but until now as I see there's more than enough overlap between the old and the new functions. If we write the code according some "best practice" that takes this into consideration + how the optimizer work, then we should be fine IMHO.

  • As a dev, I don't want to worry about stuff being inappropriately optimized away

    My point was that this is an optimization I've not yet implemented (but plan to in the next release).

    On the other hand, if something is inappropriately optimized away thats a bug; and yes, of course bugs are always possible. But thats true of your pre-processor, and the monkeyc compiler, and any system you use to program with.

  • the only problem you fear is that deprecated functions will eventually be removed

    I think we're mostly in agreement. My position is that 

    1.  if the sdk/compiler.json says a device does not support a newer api, its safe to convert the has test to false; because worst case the device eventually gets the api, and the only consequence is that the app continues to behave as if it didn't get it. So I'll put the optimization under an option, so that developers can choose whether they want that behavior or not.
    2. if we have a list of devices that are guaranteed to have a certain minimum sdk (which Garmin doesn't explicitly provide, but we can figure out, at least for certain devices) we could convert has tests to true. But now if Garmin deprecates and removes the api, the app will crash on devices that get the new firmware.

    But you seem to be saying that on point 2 a careful developer could avoid any issues? I'm not sure how? I suppose I could give you the option to do the optimization, and as long as we assume Garmin goes through a "deprecated" stage before actually removing any given api, and as long as you rebuild/redeploy your app every time Garmin deprecates another api, it should be safe? [edit: Im assuming I wouldn't do the "true" optimization for any deprecated api]

  • If we look at AppBase.getProperty() [since: 1.0.0, and up to, not including: 4.0.0) and Properties.getValue() [since: 2.4.0) as an example then I see an overlap. So I'm pretty sure that there are a few types of devices:

    1. [1.0.0 - 2.4.0): these can only use getPropery, so no need for getValue code

    2. [1.0.0 - 2.4.0) union [2.4.0 - 4.0.0) (but I doublt if any of them will ever reach 4.0.0): I don't know if there are any devices like this, but if yes then these could also use the getProperty and eliminate the getValue

    3. [2.4.0 - 4.0.0): these can use getValue and eliminate getProperty OR use getProperty and eliminate getValue, so the developer should decide based on features, code size, whatever is important for him.

    4. [2.4.0 - 4.0.0) union [4.0.0): these can also use only getValue and eliminate getProperty

    5. [4.0.0): these can only use getValue and not need getProperty

    6. In theory there could be a problematic group but I doubt if there will ever be any device that started as 2.3.x (or less) and it can be upgraded to 4.0.0. If there is such device then t should either decide to remove the feature from the old or the new CIQ versions (If let's say it's an optional feature like possibility to display the body battery on a watchface) or keep both code and use has.

    So if we assume that there's no device in 6 there's no need to use has :)
    But I agree this can only be done manually on a feature by feature base...

  • I found something that IMHO can be optimized easily:

    const LABEL_TOP = 0;
    
    var color = Graphics.COLOR_BLACK;
    var labelY = mLabelY + LABEL_TOP;

    currently the optimized code looks like:

    var pre_0 = 0;
    var color = pre_0 as Toybox.Graphics.ColorValue;
    var labelY = mLabelY + pre_0;

    I'm pretty sure that you could easily eliminate the "+ pre_0" from the generated code.

  • I'm pretty sure that you could easily eliminate the "+ pre_0" from the generated code

    You would think.

    It *is* something I'd like to do, but it requires writing a type checker. If mLabelY was a string, removing the "+ 0" would change the behavior...

    Assuming mLabelY has a declared type, I could just add an option to trust declared types, and I've been thinking about that as a stop gap.

  • Sure it has! I use -l 3 ;)

  • In theory there could be a problematic group but I doubt if there will ever be any device that started as 2.3.x (or less) and it can be upgraded to 4.0.0

    Garmin explicitly said that only devices which released with CIQ 4 will ever have CIQ 4.

    That's why we have the two-track CIQ version system AKA System 4/5/6. CIQ 3 devices stay on CIQ 3 and only CIQ 4 devices will ever have CIQ 4, but Garmin still wants to add features to both sets of devices.