In this update
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.
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:
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.
alias
statementsWe 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:
After:
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.
We also changed brightscript.bsdk
to always store the path using forward slashes so the value works cross-platform.
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.
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:
After:
stagingDir
relative to the bsconfig.json fileWe 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:
"rootDir": "../../src",
"stagingDir": "../../build"
{
"extends": "./node_modules/shared-config/bsconfig.json",
}
The new results would be:
$ rootDir: {cwd-dir}/src
$ stagingDir: {cwd-dir}/build
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:
After:
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.
To align with the BrighterScript alpha releases alpha.29
and alpha.30
, we’ve fixed a few bugs caused by breaking changes.
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.
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
NOT
for numeric valuesWe 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>
roPath
component and fixed Interpreter ComparisonsWe added several fixes to more closely align the emulator with the brightscript runtime:
roPath
VisitBinary
) to match Roku behavior, throw error with Type MismatchAND
and OR
to properly compare Boolean and NumbersPrior 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
roByteArray
componentWe implemented roByteArray in BRS, which is also the base for several other missing components.
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.
roArray
methodsWe’ve implemented more missing features to roArray
, including the .slice()
, .capacity()
and .isResizable()
methods.
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.
.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.
getAudioDecodeInfo
method nameWe fixed a typo with the method name getAudioDecodeInfo
was wrongly spelled getAudioDecoderInfo
.
getSupportedGraphicsResolutions
return typeWe fixed a bug where getSupportedGraphicsResolutions
was incorrectly returning an associative array when the correct is a roArray
.
Continue For/While
statementsWe 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
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
Range
and Column
cachingLast 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
:
Caching disabled on local
:
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);
m
type with the typecast statementWe 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
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
.
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.
Alias
StatementWe’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
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;
}
We fixed a bug in the type system where using previous parameters in later parameter default expressions were incorrectly showing as errors.
roSgNodeContentNode
fields and other small fixesWe’ve added the roSGNodeContentNode
information to tye type system, and also made a few additional fixes:
Video
nodeSometimes 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
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:
After:
cannotFindName
diagnosticWe added additional details for the cannotFindName
diagnostic to clarify the type that is missing the item in question
Before:
After:
.brs
filesAlthough 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
We fixed a bug where function parameters were sometimes missing from completion results.
Before:
After:
Added support for the roSGNode.update()
function as it’s not properly documented in the Roku docs so our docs processor missed it.
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. 🤞
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:
After:
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
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.
We’re continuing to make progress on the v4 rewrite of roku-deploy we started last month
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
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:
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.DS_Store
or Thumbs.db
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}'
]
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.
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);
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:
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 typePreviously, 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
isInstance
flagWe’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
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.
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:
typecast m as <Type>
statement (PR #1140)CreateObject
function call implies correct return type (PR #1154)Alias
Statement (PR #1151)roSgNodeContentNode
fields (PR #1159)AstNode.getEndTrivia()
(PR #1184)isInstance
flag (PR #1187)Contributions to roku-deploy:
Contributions to roku-debug:
Contributions to brighterscript-formatter:
Contributions to bslint:
Contributions to ropm:
Contributions to brs:
help
, clear
and vars
(PR #49)roPath
component and fixed Interpreter Comparisons (PR #50)roByteArray
component (PR #53)Box()
function and improved boxing (PR #54)slice()
method in roArray
under ifArraySlice
(PR #61)ifArraySizeInfo
in roArray
(PR #62)print
semi-colon behavior (PR #67).todo
and .skip
(PR #71)roDeviceInfo
method typo and some test cases (PR #69)Continue For/While
statements (PR #70)try...catch
and throw
(PR #72)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 @arturocuya, powered by Astro
by