Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make roSGNode and roSGNodeNode the same #984

Merged
merged 5 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/.cache.json

Large diffs are not rendered by default.

44 changes: 43 additions & 1 deletion src/Program.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2564,7 +2564,7 @@ describe('Program', () => {
});

describe('global symbol table', () => {
it('adds primitves', () => {
it('adds primitives', () => {
const table = program.globalScope.symbolTable;
const opts = { flags: SymbolTypeFlag.typetime };
const rtOpts = { flags: SymbolTypeFlag.runtime };
Expand All @@ -2588,6 +2588,7 @@ describe('Program', () => {

expectTypeToBe(table.getSymbolType('roRegistry', opts), InterfaceType);
expectTypeToBe(table.getSymbolType('roRegistry', opts).getMemberType('GetSectionList', rtOpts), TypedFunctionType);

});

it('adds brightscript interfaces', () => {
Expand Down Expand Up @@ -2634,6 +2635,47 @@ describe('Program', () => {
expectTypeToBe(table.getSymbolType('roSGNodeTimer', opts).getMemberType('duration', rtOpts), FloatType);
expectTypeToBe(table.getSymbolType('roSGNodeTimer', opts).getMemberType('fire', rtOpts), DynamicType);

expectTypeToBe(table.getSymbolType('roSGNodeNode', opts), ComponentType);
expectTypeToBe(table.getSymbolType('roSGNodeNode', opts).getMemberType('id', rtOpts), StringType);
expectTypeToBe(table.getSymbolType('roSGNodeNode', opts).getMemberType('change', rtOpts), AssociativeArrayType);
});

it('roSGNode and roSGNodeNode are type equivalent', () => {
const table = program.globalScope.symbolTable;
const opts = { flags: SymbolTypeFlag.typetime };
const roSGNodeType = table.getSymbolType('roSGNode', opts);
const roSGNodeNodeType = table.getSymbolType('roSGNodeNode', opts);

expectTypeToBe(roSGNodeType, ComponentType);
expectTypeToBe(roSGNodeNodeType, ComponentType);
let data = {};
const first = roSGNodeType.isTypeCompatible(roSGNodeNodeType, data);
expect(first).to.be.true;
data = {};
const second = roSGNodeNodeType.isTypeCompatible(roSGNodeType, data);
expect(second).to.be.true;
});

it('components are compatible with roSGNode and roSGNodeNode', () => {
const table = program.globalScope.symbolTable;
const opts = { flags: SymbolTypeFlag.typetime };
const roSGNodeType = table.getSymbolType('roSGNode', opts);
const roSGNodeNodeType = table.getSymbolType('roSGNodeNode', opts);

const labelType = table.getSymbolType('roSGNodeLabel', opts);
const posterType = table.getSymbolType('roSGNodePoster', opts);
const rowListType = table.getSymbolType('roSGNodeRowList', opts);
const taskType = table.getSymbolType('roSGNodeTask', opts);

expect(roSGNodeType.isTypeCompatible(labelType)).to.be.true;
expect(roSGNodeType.isTypeCompatible(posterType)).to.be.true;
expect(roSGNodeType.isTypeCompatible(rowListType)).to.be.true;
expect(roSGNodeType.isTypeCompatible(taskType)).to.be.true;

expect(roSGNodeNodeType.isTypeCompatible(labelType)).to.be.true;
expect(roSGNodeNodeType.isTypeCompatible(posterType)).to.be.true;
expect(roSGNodeNodeType.isTypeCompatible(rowListType)).to.be.true;
expect(roSGNodeNodeType.isTypeCompatible(taskType)).to.be.true;
});
});

Expand Down
9 changes: 8 additions & 1 deletion src/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export class Program {
}
nodeType = new ComponentType(nodeData.name, parentNode);
nodeType.addBuiltInInterfaces();
if (nodeData.name === 'Node') {
// Add `roSGNode` as shorthand for `roSGNodeNode`
this.globalScope.symbolTable.addSymbol('roSGNode', { description: nodeData.description }, nodeType, SymbolTypeFlag.typetime);
}
this.globalScope.symbolTable.addSymbol(nodeName, { description: nodeData.description }, nodeType, SymbolTypeFlag.typetime);
} else {
nodeType = this.globalScope.symbolTable.getSymbolType(nodeName, { flags: SymbolTypeFlag.typetime }) as ComponentType;
Expand Down Expand Up @@ -166,7 +170,10 @@ export class Program {
for (const componentData of Object.values(components) as BRSComponentData[]) {
const nodeType = new InterfaceType(componentData.name);
nodeType.addBuiltInInterfaces();
this.globalScope.symbolTable.addSymbol(componentData.name, { description: componentData.description }, nodeType, SymbolTypeFlag.typetime);
if (componentData.name !== 'roSGNode') {
// we will add `roSGNode` as shorthand for `roSGNodeNode`, since all roSgNode components are SceneGraph nodes
this.globalScope.symbolTable.addSymbol(componentData.name, { description: componentData.description }, nodeType, SymbolTypeFlag.typetime);
}
}

for (const ifaceData of Object.values(interfaces) as BRSInterfaceData[]) {
Expand Down
2 changes: 2 additions & 0 deletions src/bscPlugin/completions/CompletionsProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ export class CompletionsProcessor {
return CompletionItemKind.EnumMember;
} else if (isNamespaceType(type)) {
return CompletionItemKind.Module;
} else if (isComponentType(type)) {
return CompletionItemKind.Interface;
}
if (areMembers) {
return CompletionItemKind.Field;
Expand Down
4 changes: 2 additions & 2 deletions src/roku-types/data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedDate": "2023-11-24T19:45:56.208Z",
"generatedDate": "2023-12-08T13:21:05.078Z",
"nodes": {
"animation": {
"description": "Extends [**AnimationBase**](https://developer.roku.com/docs/references/scenegraph/abstract-nodes/animationbase.md\n\nThe Animation node class provides animations of renderable nodes, by applying interpolator functions to the values in specified renderable node fields. For an animation to take effect, an Animation node definition must include a child field interpolator node ([FloatFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/floatfieldinterpolator.md\"FloatFieldInterpolator\"), [Vector2DFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/vector2dfieldinterpolator.md\"Vector2DFieldInterpolator\"), [ColorFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/colorfieldinterpolator.md\"ColorFieldInterpolator\")) definition for each renderable node field that is animated.\n\nThe Animation node class provides a simple linear interpolator function, where the animation takes place smoothly and simply from beginning to end. The Animation node class also provides several more complex interpolator functions to allow custom animation effects. For example, you can move a graphic image around the screen at differing speeds and curved trajectories at different times in the animation by specifying the appropriate function in the easeFunction field (quadratic and exponential are two examples of functions that can be specified). The interpolator functions are divided into two parts: the beginning of the animation (ease-in), and the end of the animation (ease-out). You can apply a specified interpolator function to either or both ease-in and ease-out, or specify no function for either or both (which is the linear function). You can also change the portion of the animation that is ease-in and ease-out to arbitrary fractional values for a quadratic interpolator function applied to both ease-in and ease-out.",
Expand Down Expand Up @@ -3857,7 +3857,7 @@
{
"accessPermission": "READ_WRITE",
"default": "",
"description": "Specifies the bitmap file used for the focus indicator when the list has focus. In most cases, this should be a \\[9-patch image\\](https://go.roku.com/rowlist-9-patch-image) that specifies both expandable regions as well as margins. Only set this field to specify a custom bitmap that differs in appearance from the default bitmap.",
"description": "Specifies the bitmap file used for the focus indicator when the list has focus. In most cases, this should be a \\[9-patch image\\](https://image.roku.com/ZHZscHItMTc2/focus-9.png) that specifies both expandable regions as well as margins. Only set this field to specify a custom bitmap that differs in appearance from the default bitmap.",
"name": "focusBitmapUri",
"type": "uri"
},
Expand Down
16 changes: 11 additions & 5 deletions src/types/BuiltInInterfaceAdder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class BuiltInInterfaceAdder {
const lowerIfaceName = iface.name.toLowerCase();
const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
for (const method of ifaceData.methods ?? []) {
const methodFuncType = this.buildMethodFromDocData(method, overrides);
const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
builtInMemberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, SymbolTypeFlag.runtime);
}
for (const property of ifaceData.properties ?? []) {
Expand All @@ -62,16 +62,22 @@ export class BuiltInInterfaceAdder {
}
}

private static buildMethodFromDocData(method: BRSInterfaceMethodData, overrides?: Map<string, BuiltInInterfaceOverride>): TypedFunctionType {
private static buildMethodFromDocData(method: BRSInterfaceMethodData, overrides?: Map<string, BuiltInInterfaceOverride>, thisType?: BscType): TypedFunctionType {
const override = overrides?.get(method.name.toLowerCase());
const returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
let returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
if (!returnType && method.returnType.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
returnType = thisType;
}
const methodFuncType = this.typedFunctionFactory(returnType);
methodFuncType.name = method.name;
methodFuncType.isVariadic = method.isVariadic ?? false;
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < method.params.length; i++) {
const param = method.params[i];
const paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
let paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
if (!paramType && param.type.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
paramType = thisType;
}
methodFuncType.addParameter(param.name, paramType, !param.isRequired);
}
return methodFuncType;
Expand Down Expand Up @@ -156,7 +162,7 @@ export class BuiltInInterfaceAdder {
memberTable.addSymbol(field.name, { description: field.description, completionPriority: 1 }, util.getNodeFieldType(field.type, lookupTable), SymbolTypeFlag.runtime);
}
for (const method of builtInNode.methods ?? []) {
const methodFuncType = this.buildMethodFromDocData(method);
const methodFuncType = this.buildMethodFromDocData(method, null, thisType);
memberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, SymbolTypeFlag.runtime);
}
}
Expand Down