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]

  • but the Dictionary.get does return null if the key is not in the dictionary

    Yes. I'll try to get a fix for this and the Char comparison issue out in the next day or so.

  • Actually there's another bug (from the end-user's perspective) in this code, though not many users will find it:

    The pmc-analysis assumes that value.weak() returns WeakReference and thus doesn't complain that I add it to the Dictionary<Symbol, WeakReference>. Since that is what is written incorrectly in the documentation, I might imagine that it's also incorrect in the mir files? Anyway, I don't know if you have a way to override this if it's indeed a bad mir file, but the reality is (if you read the documentation Note, not only the Returns) it returns... well, it's complicated... WeakReference or Any is not a bad guess, and I think it's good enough for pmc-analysis (in theory Any could be a bit narrowed down to the immutable types)

  • Another bug, this time in the monkey.jungle parser. I noticed today that the syntax higlighting and especially the click and go to usage / definition stopped working. I realized now that it's because of a variable I introduced in my monkey.jungle:

    DEFAULT_LANG=hun
    base.sourcePath = source;gen/$(DEFAULT_LANG)/source;lang/$(DEFAULT_LANG)/source
    base.resourcePath = resources;gen/$(DEFAULT_LANG)/resources
    

    It both puts a red error on the $ in the jungle file, and it causes the other things not to work either,

  • I realized that one of the "classic" optimizations you could do (either in code or byte-code) is to replace container.get(k) with container[k], and container.put(k, v) with container[k] = v

    I am surprised this is not done yet. (also strange that the 2 create different bytecodes in the Garmin compiler)

  • eplace container.get(k) with container[k], and container.put(k, v) with container[k] = v

    Does this work if the keys don't already exist? The long form is "new" key safe for sure, I remember (maybe incorrectly) that the short form doesn't behave well in that case.

  • Another bug, this time in the monkey.jungle parser

    Sorry - looks like I missed this altogether. According to my parser, each component in a sourcePath etc is either a literal, or a variable, but not a mix. This looks like it will be surprisingly hard to fix - but I'll see what I can do at the w/e.

  • I am surprised this is not done yet. (also strange that the 2 create different bytecodes in the Garmin compiler)

    Remember that Garmin's compiler doesn't really optimize based on type knowledge. So it generates the same code whether the type checker is enabled or not. Given that, it has no way to do this optimization.

    But even if it did, they're not necessarily equivalent. Suppose I extend Dictionary with my own class that overrides put, and then pass a MyDictionary object to a function that takes a Dictionary. If I change d.put(foo, bar) into d[foo]=bar it won't call my override. In fact, I just tried this, and the d[foo]=bar syntax doesn't work on a user defined class that extends Dictionary (it crashes), but put does.

  • Yes, container[k] is null if container is a dictionary, and it doesn't contain k.

  • I didn't think about inheritance. I thought (at the beginning) that [] is a syntactic sugar for get, put. I still don't understand though what is the difference when there's no inheritance, just simple Dictionary or array, and even more so: Garmin please add this to the documentation of Dictionary and Array, please.

    And for the optimizer: since you do know about the type, I think it's worth to try optimize it, it's a relatively big gain (8, 9 bytes)

  • I found some old code in github and trying to adjust it for strict typecheck. I run into the following interesting issue:

    original code:

        function foo() {
            longPressTimer = new Timer.Timer();
            longPressTimer.start( method(:onNextPage), LONG_PRESS_UPDATE_FREQUENCY, true );
        }
    
        function onNextPage() {
            return true;
        }

    added types:

        function foo() as Void {
            longPressTimer = new Timer.Timer();
            longPressTimer.start( method(:onNextPage) as Method() as Void, LONG_PRESS_UPDATE_FREQUENCY, true );
        }
    
        function onNextPage() as Boolean {
            return true;
        }

    And I get the following warning, which is true, but I'm not sure what to do with it:

    The type Method() as Boolean cannot be converted to Method() as Null because they have nothing in common [pmc-analysis]

    On one hand the original code worked, the new code works, so I tend to think that this is a very specific case and the pmc-analyzer could be adjusted to let this.

    On the other hand, I don't know if it's always OK. What if the "bytecode" thinks that there is no return value, and thus doesn't remove the return value from the stack so it's left there?