In this update

  1. Overview
  2. We need your help
  3. Editor
  4. Add Roku REPL panel
  5. Adds syntax highlighting for alias statements
  6. Fix bugs in the language server version picker
  7. Fix broken device view
  8. Improve “Connect manually” UI layout
  9. BrighterScript
  10. Resolve stagingDir relative to the bsconfig.json file
  11. Move function calls to separate diagnostic
  12. Fix crash when diagnostic is missing range
  13. Community Tools
  14. bslint
  15. Brighterscript Alpha 29 & 30 updates
  16. brs
  17. Added colorization and commands help, clear and vars
  18. Support bitwise NOT for numeric values
  19. Implemented roPath component and fixed Interpreter Comparisons
  20. Implemented roByteArray component
  21. Improved boxing functionality and implemented Box()
  22. Add missing roArray methods
  23. More accurate component constructor error handling
  24. Fixed Unit Tests .todo and .skip
  25. Fix getAudioDecodeInfo method name
  26. Fix getSupportedGraphicsResolutions return type
  27. Implemented Continue For/While statements
  28. Implement try...catch and throw
  29. Preview features
  30. BrighterScript
  31. Revert Range and Column caching
  32. No more validation for roAssociativeArray
  33. Declare m type with the typecast statement
  34. Fixes CompletionItemKinds for typed variables
  35. Fixes includes in self closing components
  36. New Alias Statement
  37. Add AstNode visitor function for createVisitor
  38. Fixes issue with default params referencing previous ones
  39. Adds roSgNodeContentNode fields and other small fixes
  40. Fix roDateTime compatibility issue
  41. Hovers on LHS of assignments do symbol type lookups
  42. Improved the cannotFindName diagnostic
  43. Relax validation for type unions in .brs files
  44. Fix missing parameter in completions
  45. Adds roSGNode Update() overload
  46. Fix several lsp crashes for v1
  47. Allows numbers to be passed to a function that takes a bool
  48. Adds Conditional Compilation Statements to the AST
  49. Alias semantic tokens
  50. roku-deploy
  51. Change documentation
  52. Update files array
  53. Enhanced logging levels
  54. For Contributors
  55. BrighterScript v1
  56. Adds AstNode.getEndTrivia()
  57. Diagnostics Refactor: DiagnosticManager
  58. CreateObject function call implies correct return type
  59. Adds isInstance flag
  60. Upgrade BrighterScript to @rokucommunity/logger
  61. Thank you

Overview

Welcome to the May 2024 edition of “What’s New in RokuCommunity.” Please consider subscribing to stay up to date with what’s happening in RokuCommunity.

We need your help

The RokuCommunity projects are maintained by a relatively small group of developers (mostly volunteers), and we have a growing list of unresolved issues. We need your help! There are many different ways you can contribute. Whether it’s addressing bugs, improving documentation, introducing new features, or simply helping us manage our expanding list of GitHub issues, your involvement would be greatly appreciated. We are more than happy to guide you in finding the most suitable contribution method that aligns with your interests. To learn more about how you can contribute, feel free to reach out to us on Slack, or explore the existing GitHub issues:

Editor

Add Roku REPL panel

We’ve added a new panel called “BrightScript REPL”, which enables you to run arbitrary brs code on-device (as long as the dev channel is running and RDB is active). This can be incredibly useful for running code when not stopped at a breakpoint in the debugger. The way it works is by embedding the REPL code into a small component library and then executing the code from that component library, with some help from the RTA on-device component.

Apr-24-2024 11-24-20

Adds syntax highlighting for alias statements

We added syntax highlighting support for the new alias statement added in the BrighterScript v1 alphas. The alias statement helps mitigate issues when shadowed variables and name collisions.

Before: image

After: image

Fix bugs in the language server version picker

We fixed a crash in the brighterscript version picker whenever ${workspaceFolder}/node_modules/brighterscript wasn’t found. Now it gracefully recovers, and simply excludes any versions that weren’t able to be inspected.

image

We also changed brightscript.bsdk to always store the path using forward slashes so the value works cross-platform.

image

Fix broken device view

We fixed a bug in the Roku Device View panel that had broken some time in the past month. It was completely non-functional, so this update resolved that issue and now it’s operational again.

image

Improve “Connect manually” UI layout

We updated the UI design of the “connect manually” section in several of the Roku panels to leverage the standard vscode themed inputs and UI elements, so this section should now feel more in sync with the rest of the VSCode UI.

Before: image (4)

After: image (5)

BrighterScript

Resolve stagingDir relative to the bsconfig.json file

We updated the stagingDir BrighterScript option to resolve relative to the bsconfig.json it was defined in. This is technically a breaking change, but we don’t believe many developers will be impacted since this was mostly an issue with ancestor bsconfig.json files used via the extends bsconfig option. This change allows ancestor bsconfig.json files to specify stagingDir relative to themselves rather than needing additional knowledge about where the child bsconfig.json would be located.

Here’s an example. Given the two bsconfig.json files below:

  1. ./node_modules/shared-config/bsconfig.json
  "rootDir": "../../src",
  "stagingDir": "../../build"
  1. ./bsconfig.json
{
  "extends": "./node_modules/shared-config/bsconfig.json",
}

The new results would be:

$ rootDir: {cwd-dir}/src
$ stagingDir: {cwd-dir}/build

Move function calls to separate diagnostic

We moved cannot find name (1001) diagnostics for function calls into their own diagnostic code (1140).

We’ve found that the biggest pain point when a new development team starts using the VSCode extension is when there are functions called in a utility file that are only imported by some of the scopes that use that utility file. This often results in diagnostics like ”Cannot find name 'testFunction'. Not defined in scope 'components\SomeComponent.xml'. While the diagnostic is technically valid, typically developers have already tested their code and know that logically these functions will only be called in safe situations. Unfortunately the brighterscript compiler is unable to determine this, so we feel it’s better to show a diagnostic.

By splitting up "cannot find variable" and "cannot find function" concepts into separate diagnostic codes, this will allow dev teams to more slowly migrate to best practices in their projects by ignoring code the function diagnostic 1140 without also losing the benefits of still being warned about using unknown variables.

Before: image

After: image

Fix crash when diagnostic is missing range

We fixed a crash during the brighterscript transpiling when certain diagnostics were missing ranges. BrighterScript always includes a range in the diagnostics it produces so these were typcially bugs in plugins. We still don’t want it crashing so in these situations we will print the message and omit the missing range.

image

Community Tools

bslint

Brighterscript Alpha 29 & 30 updates

To align with the BrighterScript alpha releases alpha.29 and alpha.30, we’ve fixed a few bugs caused by breaking changes.

  • Use DiagnosticManager to add diagnostics
  • Update walk for comments

As we stated a few months ago, every version of bslint v1 alpha aligns with a corresponding brighterscript alpha, so be sure you’re keeping those versions in sync.

brs

Added colorization and commands help, clear and vars

We added colorization to the brs emulator when run via the terminal and fixed a bug where print was adding a space when semi-colon was used.

We also added some new CLI commands such as help, clear, and vars

Untitled

Support bitwise NOT for numeric values

We added support for the bitwise NOT when used preceeding numeric values. Check out brs#51 and brs#23 for more information.

Prior to this change:

brs> print not  0
REPL(1,6-9): Attempting to NOT non-boolean value.
                            value type: Integer
brs> print not  1
REPL(1,6-9): Attempting to NOT non-boolean value.
                            value type: Integer
brs> print not -1
REPL(1,6-9): Attempting to NOT non-boolean value.
                            value type: Integer
brs> print not  2
REPL(1,6-9): Attempting to NOT non-boolean value.
                            value type: Integer
brs> print not -2
REPL(1,6-9): Attempting to NOT non-boolean value.
                            value type: Integer
brs>

And now with the change, the output aligns with what the actual Roku device would produce:

brs> print not  0
-1
brs> print not  1
-2
brs> print not -1
 0
brs> print not  2
-3
brs> print not -2
 1
brs>

Implemented roPath component and fixed Interpreter Comparisons

We added several fixes to more closely align the emulator with the brightscript runtime:

  • Added component roPath
  • Fixed Interpreter comparison logic (in VisitBinary) to match Roku behavior, throw error with Type Mismatch
  • Fixed Interpreter AND and OR to properly compare Boolean and Numbers

Prior to this change:

brs> print 1 and true
REPL(1,6-16): Attempting to bitwise 'and' number with non-numberic value
    left: Integer
    right: Boolean

And now:

brs> print 1 and true
true

Implemented roByteArray component

We implemented roByteArray in BRS, which is also the base for several other missing components.

Improved boxing functionality and implemented Box()

We fixed a bug where boxed numbers and booleans were not being properly handled. It is now possible to compare or make arithmetic operations among them and also with with intrinsic values. We also added the missing Box() function.

Add missing roArray methods

We’ve implemented more missing features to roArray, including the .slice(), .capacity() and .isResizable() methods.

More accurate component constructor error handling

We improved exception handling when calling the components constructor. We now allow the component to not be created if wrong parameters are passed, making CreateObject() return invalid in those cases and aligning with how Roku devices handle this situation.

Fixed Unit Tests .todo and .skip

We fixed several situations where the roString behavior was not consistent with Roku. You can check out brs#71 for more information.

Fix getAudioDecodeInfo method name

We fixed a typo with the method name getAudioDecodeInfo was wrongly spelled getAudioDecoderInfo.

Fix getSupportedGraphicsResolutions return type

We fixed a bug where getSupportedGraphicsResolutions was incorrectly returning an associative array when the correct is a roArray.

Implemented Continue For/While statements

We implemented the continue for and continue while features. These were added to the Roku platform in the last year or two so it was definitely time to properly support them in brs. You can now do things like this:

fruits = ["orange", "lemon", "lime"]
for each fruit in fruits
    if fruit = "lemon"
        continue for  ' you no longer need to mess with labels and goto!
    end if
    ? fruit
end for

counter = 0
while counter < 3
    if counter = 1
        counter++
        continue while 'sweet new syntax
    end if
    ? counter
    counter++
end while

Implement try...catch and throw

We were finally able to land the try/catch functionality in brs this month, as well as support for throw and aligning some of the runtime error messages with those from the device. Check out the official Roku docs for how to use try/catch, but here’s an example:

try
    print "[subFunc] a =" a
    if a = 10
        thirdLevel()
    end if
    a = a * "" ' force a type error
catch e
    print "Error # =" e.number
    if a = 10
        e.customField = true
        throw e
    else
        print "Error message = " e.message
    end if
endtry

Preview features

BrighterScript

Revert Range and Column caching

Last year we added Range and Position caching because it seemed to improve performance. However, there was a bug where line or column numbers over 13 bits long would sometimes result in retrieving the wrong cached value (oops!). We fixed the bug in the most optimal way possible. Unfortunately with the proper caching algorithm in place, this actually slowed down performance. It’s faster to just make new Range and Location objects directly. The V8 runtime does a great job optimizing this flow, so this month we reverted the caching logic entirely. It was worth a try…

Caching enabled on local: image

Caching disabled on local: image

No more validation for roAssociativeArray

We removed validation for properties on standard typed roAssociativeArray objects, as their entire purpose is to be a generic dictionary of anything.

program.setFile<BrsFile>('source/aa.bs', `
    sub foo(someAA as roAssociativeArray)
          print someAA.whatever.whatever ' look, no diagnostics here
    end sub
`);
program.validate();
expectZeroDiagnostics(program);

Declare m type with the typecast statement

We added the ability to declare the type of m. This has been a frequently requested feature, so we’re excited to provide this capability. You can a single typecast statement to beginning of a file, namespace body, or function body, and every usage of m in that or lower scopes will be typecast as the type given.

Here’s the syntax:

typecast m as <type>

And a quick example

import "types.bs"
typecast m as Thing1

sub func1()
    x = m.value ' x is type of Thing1.value
    print x
end sub

sub func2()
    typecast m as Thing2
    x = m.value  ' x is type of Thing2.value
    print x
end sub

sub func3()
    aa = {
        innerFunc: sub()
            typecast m as Thing3
            x = m.value  ' x is type of Thing3.value
            print x
        end sub
    }
end sub

class TestKlass
    value as string

    sub method1()
        x = m.value ' x is type of TestKlass.value
        print x
    end sub

    sub method2()
        typecast m as Thing1
        x = m.value  ' x is type of Thing1.value
        print x
    end sub
end class

Fixes CompletionItemKinds for typed variables

We fixed a bug where completions for instances of interfaces were incorrectly showing the interface symbol when they should have been showing as the variable symbol. We’ve fixed this issue, so all variables that are Classes, Interfaces, Enum, functions, etc. will now properly be be displayed as variables.

In the screenshot below, runResult is a variable of some interface type. The symbol you see is for interface, but it should have the same symbol as lastRunResult. image

Fixes includes in self closing components

We fixed an issue in XML transpilation where self-closing elements that later had child elements added to them would not have those child elements transpiled. This was mostly a watch mode or language server issue, so now your in-editor experience should be more consistent.

New Alias Statement

We’ve added a new statement type called alias, which provides a way to work around variable name shadowing.

This statement also has a neat side effect: it can be used to shorthand just about any constant/enum/function you want.

alias get2 = get

namespace http
    'Do an HTTP request
    sub get()
        print get2.aa().data 'using `get2` aliased symbol here. it's now clear which item we intended to use
    end sub
end namespace

namespace get
    function aa()
        return {
            data: "abc"
        }
    end function
end namespace

transpiles to

sub http_get()
    print get_aa().data
end sub

sub get_aa()
    return {
        data: "abc"
    }
end sub

it also allows this … which is cool?:

alias lc = lcase
alias co = createObject

sub getModelTypeLower()
   lc(co("roDeviceInfo").getModelType())
end sub

it also allows this … which is also cool?:

alias abgdz = alpha.beta.gamma.delta.zeta

sub foo()
   print abgdz.someConst
end sub

Add AstNode visitor function for createVisitor

Since we removed CommentStatement and CommentExpression from the AST, there was no longer a convenient way to find comments in the AST. To help with this, we’ve added a new visitor method called AstNode to createVisitor which triggers for every single node in the AST. Just keep in mind that this fires alongside the actual node type event, so for example if you have an entry for FunctionStatement and AstNode, you’ll see that function statement show up in AstNode too.

Here’s how you use it to find comments:

import { isBrsFile, createVisitor, WalkMode, BeforeFileTranspileEvent, CompilerPlugin } from 'brighterscript';
export default function plugin() {
    return {
        name: 'removeCommentAndPrintStatements',
        beforeFileTranspile: (event: BeforeFileTranspileEvent) => {
            if (isBrsFile(event.file)) {
                event.file.ast.walk(createVisitor({
                    //register a visitor that is fired for every single node in the entier AST tree
                    AstNode: (node: AstNode, _parent, owner, key) => {
                        //get a list of all trivia for this node
                        const trivia = node.getLeadingTrivia();
                        for(let i = 0; i < trivia.length; i++) {
                            let triviaItem = trivia[i].
                            if (triviaItem.kind === TokenKind.Comment) {
                                //remove comment tokens
                                event.editor.removeProperty(trivia, i);
                            }
                        }
                    }
                }), {
                    walkMode: WalkMode.visitStatements | WalkMode.visitComments
                });
            }
        }
    } as CompilerPlugin;
}

Fixes issue with default params referencing previous ones

We fixed a bug in the type system where using previous parameters in later parameter default expressions were incorrectly showing as errors.

image

Adds roSgNodeContentNode fields and other small fixes

We’ve added the roSGNodeContentNode information to tye type system, and also made a few additional fixes:

  • adds missing fields to Video node
  • fixed descriptions when the first line is `Available Since Roku OS …”
  • better infers types coming from Roku docs, especially when a field is another Component type

Fix roDateTime compatibility issue

Sometimes certain types would get messed up if there were duplicate definitions of members in the roku docs. This typically happened during our docs ingestion script. We’ve fixed that script so that it properly handles all the overloaded methods in interfaces automatically. You can check out the changes in #1163

Here are all the overloads the docs processor script fixed automatically at the time of this change:

Fixed overloaded method ifAudioResource.trigger
Fixed overloaded method ifByteArray.writefile
Fixed overloaded method ifByteArray.readfile
Fixed overloaded method ifByteArray.appendfile
Fixed overloaded method ifByteArray.getcrc32
Fixed overloaded method ifDateTime.toisostring
Fixed overloaded method ifDraw2D.drawscaledobject
Fixed overloaded method ifFontRegistry.getdefaultfont
Fixed overloaded method ifSGNodeField.observefield
Fixed overloaded method ifSGNodeField.observefieldscoped
Fixed overloaded method ifStringOps.mid
Fixed overloaded method ifStringOps.instr
Fixed overloaded method ifStringOps.startswith
Fixed overloaded method ifStringOps.endswith
Fixed overloaded method ifToStr.tostr
Fixed overloaded method ifVideoPlayer.setdestinationrect

Hovers on LHS of assignments do symbol type lookups

Previously, the type displayed on an assignment was the type of the right-hand-side expression. We changed it so that it now does a symbolTable lookup for the token on the left-hand-side.

Before: image

After: image

Improved the cannotFindName diagnostic

We added additional details for the cannotFindName diagnostic to clarify the type that is missing the item in question

Before:

image

After:

image

Relax validation for type unions in .brs files

Although we’re very excited for the improved type validation in the v1 rewrite of brighterscript, we still have a priority to ensure compatibility with standard brightscript projects. This means wenever possible, improve the type safety and checking of brightscript files, but do not require code rework due to type strictness.

We fixed a bug that was leaking some brighterscript features into standard brightscript. To fix this we have eliminated the concept of union types for standard brightscript projects, since there’s no nice way to break away from that type without advanced type syntax (which is not available to standard projects). Whenever the brighterscript type system encounters a union type in standard brightscript projects, thise variables will now be treated as dynamic.

Code with variables that change types throughout the function body will no longer show diagnostics in .brs files:

function typeHoverTest(x as string)
    x = x.len()
    return x
end function

Fix missing parameter in completions

We fixed a bug where function parameters were sometimes missing from completion results.

Before: image

After: image

Adds roSGNode Update() overload

Added support for the roSGNode.update() function as it’s not properly documented in the Roku docs so our docs processor missed it.

image

Fix several lsp crashes for v1

We’ve added more stability to the language server by preventing several crashes related to missing range or AST properties. There’s not much to show here, but just know it should hopefully crash less now. 🤞

Allows numbers to be passed to a function that takes a bool

The BrightScript runtime supports using integers as booleans, which was an oversight in our brighterscript type validation logic. We’ve fixed the issue, and you can now properly use integers in place of booleans. We may add a bslint rule in the future to prohibit this type of coding style, but we’ll see how it all works out as v1 nears its final release.

Before: image

After: image

Adds Conditional Compilation Statements to the AST

Conditional compile directives (#if, #const, #error, etc.) are now part of the AST, and as such, the branches of the condition compiles can be walked.

There is one concession with this: #if blocks are treated as any other block, and everything in them must be self contained, or will cause a diagnostic.

That means that even though this is allowed in Brightscript:

sub foo()
# if true
end sub
#end if

it will cause some diagnostics, because the sub foo() is partially inside and outside the #if statement

Alias semantic tokens

Adds semantic tokens to aliases as they flow downstream through the program. We also fixed some bugs with aliased functions not having the correct colors because we can’t look them up with their global names since the alias name is there.

image

roku-deploy

We’re continuing to make progress on the v4 rewrite of roku-deploy we started last month

Change documentation

We updated the roku-deploy v4 branch’s documentation to align with the new designs. We’ve also added a dedicated v4 section to the docs to better help with upgrade paths. You can see these changes in #162 and #166

Update files array

We’ve finally updated the files array to include a few useful additions. These additions will hopefully reduce the need for developers to manually edit this list for standard project structures. A few notable callouts:

  • we now exclude all node_modules folders by default since those can produce zips that are hundreds of megabytes large, and definitely should not be published to Roku devices
  • exclude some of those pesky OS file system files like .DS_Store or Thumbs.db
  • we’ve included some other common folders such as locale and fonts

Here’s the original files array:

[
    'source/**/*',
    'components/**/*',
    'images/**/*',
    'manifest'
]

and here’s the new version:

[
    'source/**/*.*',
    'components/**/*.*',
    'images/**/*.*',
    'locale/**/*',
    'fonts/**/*',
    'manifest',
    '!node_modules',
    '!**/*.{md,DS_Store,db}'
]

Enhanced logging levels

We’ve enhanced the roku-deploy console logging with various levels and added additional log entries to help better track what’s happening internally. If you want to see these extra logs, set your logLevel to info or debug or trace. Check out #168 if you want to learn more.

For Contributors

BrighterScript v1

Adds AstNode.getEndTrivia()

For plugin authors, we’ve added the ability to access the trivia at the end of a block when walking the AST. This is because technically the comment is leadingTrivia on the end sub token.W e solved this by adding a new .getEndTrivia() method to the AstNode interface.

Here’s some example brightscript code showing the issue:

sub myFunc()
    ' There is a comment here
end sub

And here’s how you would access that now from the BrighterScript AST:

const endNodeComments = node.getEndTrivia().filter(t => t.kind === TokenKind.Comment);

Diagnostics Refactor: DiagnosticManager

This month we made some fairly significant changes to the diagnostics system. Now ALL diagnostics need to be registered through DiagnosticManager, publically available through Program

DiagnosticManager has the following features:

  • a single diagnostic can be attached to multiple contexts
  • contexts optionally include scope, tags or particular Ast Nodes
  • if the same diagnostic is registered with a different scope context, it is added a related information
  • individual diagnostic contexts can be cleared - if all the contexts are cleared for a diagnostic, then the diagnostic is automatically removed.

This empowers us to much more easily remove diagnostics for an entire scope without removing diagnostics that are related to multiple scopes. This also makes it much easier for plugins to add or remove diagnostics, because they can do things like this:

program.diagnostics.register(someDiagnostic, { tags: ['my-plugin'] });

//...some time later

//remove all of our diagnostics and start over
program.diagnostics.clearForTag('my-plugin');

CreateObject function call implies correct return type

Previously, everything returned from createObject was ObjectType. Now, it looks at the arguments, and if passed string literals, it is able to infer either the built-in Component/Object or a custom component:

dateTime = createObject("roDatetime") ' this is the InterfaceType `roDateTime`
dateTime.getHours() ' this is an integer


button = createObject("roSgnode", "Poster") '  this is the ComponentType `roSgNodePoster`
button.uri ' this is a string

if the args are not string literals, it still just returns ObjectType…

function getObject(objectName as string)
    return createObject(objectName) ' this is just ObjectType
end function

Adds isInstance flag

We’ve added the isInstance flag on symbol extra data for when a symbol represents an instance of a type.

This is useful for walking all variable expressions, and being able to check if that expression is an instance of the type or a reference to the actual type (or class constructor).

eg:

class Klass
end class

sub test(k as Klass)  ' k is an instance, Klass is not, but both are of ClassType("klass")
end sub

Upgrade BrighterScript to @rokucommunity/logger

A few years ago RokuCommunity released our own NodeJS logging library called @rokucommunity/logger. BrighterScript had been operating for a while with its own logger, so this month we took the opportunity to upgrade to the RokuCommunity logger. The logger output is identical, so there should be minimal issues with this upgrade.

Thank you

Last but certainly not least, a big Thank You to the following people who contributed this month:

Contributions to vscode-brightscript-language:

Contributions to brighterscript:

Contributions to roku-deploy:

Contributions to roku-debug:

Contributions to brighterscript-formatter:

Contributions to bslint:

Contributions to ropm:

Contributions to brs:

  • @lvcabral (Marcelo Lv Cabral)
    • Added to CLI: colorization and commands help, clear and vars (PR #49)
    • Added support for bitwise NOT for numeric values (PR #51)
    • Implemented roPath component and fixed Interpreter Comparisons (PR #50)
    • Implemented roByteArray component (PR #53)
    • Implemented Box() function and improved boxing (PR #54)
    • Implemented slice() method in roArray under ifArraySlice (PR #61)
    • Implement ifArraySizeInfo in roArray (PR #62)
    • Fixed print semi-colon behavior (PR #67)
    • Fixed Unit Tests .todo and .skip (PR #71)
    • Fix roDeviceInfo method typo and some test cases (PR #69)
    • Implemented Continue For/While statements (PR #70)
    • Implement try...catch and throw (PR #72)
Home What's new Slack channel Newsletter Edit this website on Github

Legal notice

The views and opinions expressed in this website and all of its pages are those from within our open source community and do not represent the views or positions of Roku, Inc. or any entities they represent.

Designed with favorite by @arturocuya, powered by Astro