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

Component statement support for <children> and field attributes #971

Open
wants to merge 28 commits into
base: in-code-component
Choose a base branch
from
Open
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5f55915
Adds the template children and field attributes to the component
iObject Nov 30, 2023
086e267
Validate annotations being send to getAnnotationValue, omit empty fie…
iObject Nov 30, 2023
4366da9
Update src/bscPlugin/fileProviders/ComponentStatementProvider.ts
iObject Nov 30, 2023
af68592
Update src/bscPlugin/fileProviders/ComponentStatementProvider.ts
iObject Nov 30, 2023
03fc579
Update src/bscPlugin/fileProviders/ComponentStatementProvider.ts
iObject Nov 30, 2023
a1630c4
Add AnnotaionExpression type
iObject Nov 30, 2023
b397e89
initialFocus
iObject Nov 30, 2023
5325d9e
Update component-statement.md to indicate requirement of method access
iObject Nov 30, 2023
fd613b6
Remove space
iObject Nov 30, 2023
fe05c8a
Remove comments
iObject Nov 30, 2023
a14a35f
Add tests
iObject Nov 30, 2023
92b99c1
Merge branch 'in-code-component' into add-component-children-and-fiel…
iObject Nov 30, 2023
6f13778
remove obj lookup in annotation lookup
iObject Nov 30, 2023
f58c40b
remove describe.only
iObject Nov 30, 2023
7c98830
remove semicolon
iObject Nov 30, 2023
cfc105a
Update src/parser/tests/statement/ComponentStatement.spec.ts
iObject Dec 1, 2023
b411e00
readd skip
iObject Dec 1, 2023
e4579cd
Merge branch 'in-code-component' into add-component-children-and-fiel…
iObject Dec 1, 2023
4508c43
Add pkgPath
iObject Dec 1, 2023
b79f963
Fix tests
iObject Dec 1, 2023
6dc246d
Fix tests part 2
iObject Dec 1, 2023
8e04a6e
update spec
iObject Dec 4, 2023
bdd0320
Merge branch 'in-code-component' into add-component-children-and-fiel…
iObject Dec 4, 2023
2e4f737
Merge branch 'add-component-children-and-field-attributes' of https:/…
iObject Dec 4, 2023
2011c02
Confirm bs files are also generated
iObject Dec 4, 2023
6972a4e
Confirm bs files are also generated
iObject Dec 4, 2023
dd48bb2
Update xml template test
iObject Dec 4, 2023
b87c072
Merge branch 'in-code-component' into add-component-children-and-fiel…
iObject Jan 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions src/bscPlugin/fileProviders/ComponentStatementProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Cache } from '../../Cache';
import * as path from 'path';
import { util } from '../../util';
import type { ProvideFileEvent } from '../../interfaces';
import { isDottedGetExpression, isFieldStatement, isMethodStatement, isVariableExpression } from '../../astUtils/reflection';
import { isDottedGetExpression, isFieldStatement, isMethodStatement, isVariableExpression, isLiteralExpression, isTemplateStringExpression } from '../../astUtils/reflection';
import { createFunctionStatement, createFunctionExpression, createDottedSetStatement, createVariableExpression } from '../../astUtils/creators';
import type { Statement } from '../../parser/AstNode';
import { TokenKind } from '../../lexer/TokenKind';
Expand All @@ -27,7 +27,7 @@ export class ComponentStatementProvider {
const cache = new Cache<string, string>();
file.ast.walk(createVisitor({
ComponentStatement: (node) => {
//force the desetPath for this component to be within the `pkg:/components` folder
//force the destPath for this component to be within the `pkg:/components` folder
const destDir = cache.getOrAdd(file.srcPath, () => {
return path.dirname(file.destPath).replace(/^(.+?)(?=[\/\\]|$)/, (match: string, firstDirName: string) => {
return 'components';
Expand Down Expand Up @@ -56,26 +56,76 @@ export class ComponentStatementProvider {

//declare interface field
} else if (isFieldStatement(member) && member.accessModifier?.text.toLowerCase() === 'public') {
return `<field id="${member.name.text}" type="${member.typeExpression.getName()}" />`;
let fieldAttributes = {
id: member.name.text,
type: member.typeExpression.getName(),
alias: this.getAnnotationValue(member.annotations?.filter(x => x.name.toLowerCase() === 'alias')),
onChange: this.getAnnotationValue(member.annotations?.filter(x => x.name.toLowerCase() === 'onchange')),
alwaysNotify: this.getAnnotationValue(member.annotations?.filter(x => x.name.toLowerCase() === 'alwaysnotify')) === 'true' ? 'true' : ''
};

let field = '<field';
Object.keys(fieldAttributes).forEach(attribute => {
let value = fieldAttributes[attribute];
// Only add a attribute if the value is not empty.
if (value !== '') {
field += ` ${attribute}="${fieldAttributes[attribute]}"`;
}
});
field += ' />';
return field;
} else {
return '';
}
}).filter(x => !!x);

let componentChildren = '';
const template = statement.annotations?.find(x => x.name.toLowerCase() === 'template');
if (template) {
// TODO: Better strip of component and children elements.
componentChildren = `<children>${this.getAnnotationValue([template]).replaceAll('<component>', '').replaceAll('</component>', '').replaceAll('<children>', '').replaceAll('</children>', '')}</children>`;
}

xmlFile.parse(undent`
<component name="${name}" extends="${statement.getParentName(ParseMode.BrightScript) ?? 'Group'}">
<script uri="${util.sanitizePkgPath(file.destPath)}" />
<script uri="${util.sanitizePkgPath(codebehindFile.destPath)}" />
${interfaceMembers.length > 0 ? '<interface>' : ''}
${interfaceMembers.join('\n ')}
${interfaceMembers.length > 0 ? '</interface>' : ''}
${componentChildren}
</component>
`);


this.event.files.push(xmlFile);
}

private getAnnotationValue(annotations: any) {
iObject marked this conversation as resolved.
Show resolved Hide resolved
let response = [];
if (annotations !== undefined) {
annotations.forEach(a => {
let args = a?.call?.args[0];
if (isVariableExpression(args) || isDottedGetExpression(args)) {
response.push(args.name.text);
} else if (isLiteralExpression(args)) {
let values = args?.token?.text.replaceAll('\"', '').replaceAll(' ', '').split(',');
response = response.concat(values);
} else if (isTemplateStringExpression(args)) {
let textOutput = '';
args.quasis[0]?.expressions?.forEach((a: { token: { text: string } }) => {
textOutput += a.token.text;
});
response.push(textOutput);
}
});
iObject marked this conversation as resolved.
Show resolved Hide resolved
iObject marked this conversation as resolved.
Show resolved Hide resolved

response = response.filter((item, index) => {
return response.indexOf(item) === index;
});
}
return response.join(', ');
}

private registerCodebehind(name: string, statement: ComponentStatement, destDir: string) {
//create the codebehind file
const file = this.event.fileFactory.BrsFile({
Expand Down Expand Up @@ -120,7 +170,7 @@ export class ComponentStatementProvider {
initFunc.func.body.statements.unshift(...initStatements);
}

//TODO these are hacks that we need until scope has been refactored to leverate the AST directly
//TODO these are hacks that we need until scope has been refactored to leverage the AST directly
file.parser.invalidateReferences();
// eslint-disable-next-line @typescript-eslint/dot-notation
file['findCallables']();
Expand Down
Loading