Skip to content

Commit

Permalink
Include missing transpiled comments (#1278)
Browse files Browse the repository at this point in the history
* Transpile comments above annotations for common statement types.

* Fix some missing transpiled comments for namespaced calls

* Fix lint issue
  • Loading branch information
TwitchBronBron authored Aug 13, 2024
1 parent d262997 commit 58ad447
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 13 deletions.
144 changes: 144 additions & 0 deletions src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2509,6 +2509,150 @@ describe('BrsFile', () => {
`, 'trim', 'source/main.bs');
});

it('includes annotation comments for class', async () => {
await testTranspile(`
'comment1
@annotation
'comment2
@annotation()
'comment3
class Beta
end class
`, `
function __Beta_builder()
instance = {}
instance.new = sub()
end sub
return instance
end function
'comment1
'comment2
'comment3
function Beta()
instance = __Beta_builder()
instance.new()
return instance
end function
`, undefined, 'source/main.bs');
});

it('includes annotation comments for function', async () => {
await testTranspile(`
'comment1
@annotation
'comment2
@annotation()
'comment3
function alpha()
end function
`, `
'comment1
'comment2
'comment3
function alpha()
end function
`, undefined, 'source/main.bs');
});

it('includes annotation comments for enum', async () => {
await testTranspile(`
'comment1
@annotation
'comment2
@annotation()
'comment3
enum Direction
up = "up"
end enum
`, `
'comment1
'comment2
'comment3
`, undefined, 'source/main.bs');
});

it('includes annotation comments for const', async () => {
await testTranspile(`
'comment1
@annotation
'comment2
@annotation()
'comment3
const direction = "up"
`, `
'comment1
'comment2
'comment3
`, undefined, 'source/main.bs');
});

it('includes annotation comments for empty namespaces', async () => {
await testTranspile(`
'comment1
@annotation
'comment2
@annotation()
'comment3
namespace alpha
'comment4
@annotation
'comment5
@annotation()
'comment6
namespace beta
end namespace
end namespace
`, `
'comment1
'comment2
'comment3
'comment4
'comment5
'comment6
`, undefined, 'source/main.bs');
});

it('includes comments above namespaced method call', async () => {
await testTranspile(`
sub main()
'do nothing
utils.noop()
end sub
namespace utils
sub noop()
end sub
end namespace
`, `
sub main()
'do nothing
utils_noop()
end sub
sub utils_noop()
end sub
`, undefined, 'source/main.bs');
});

it('includes comments above inferred namespace function call', async () => {
await testTranspile(`
namespace utils
sub test()
'do nothing
noop()
end sub
sub noop()
end sub
end namespace
`, `
sub utils_test()
'do nothing
utils_noop()
end sub
sub utils_noop()
end sub
`, undefined, 'source/main.bs');
});

it('keeps end-of-line comments with their line', async () => {
await testTranspile(`
function DoSomething() 'comment 1
Expand Down
10 changes: 10 additions & 0 deletions src/parser/BrsTranspileState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Editor } from '../astUtils/Editor';
import type { BrsFile } from '../files/BrsFile';
import type { ClassStatement, ConditionalCompileStatement } from './Statement';
import { TranspileState } from './TranspileState';
import type { Statement } from './AstNode';

export class BrsTranspileState extends TranspileState {
public constructor(
Expand Down Expand Up @@ -45,4 +46,13 @@ export class BrsTranspileState extends TranspileState {
* Do not transpile leading comments
*/
public skipLeadingComments = false;

/**
* Transpile all leading trivia for a given statement, including comments mixed between annotations
*/
public transpileAnnotations(node: Statement) {
return (node?.annotations ?? []).map(x => {
return x.transpile(this);
});
}
}
12 changes: 10 additions & 2 deletions src/parser/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ export class CallExpression extends Expression {

//transpile the name
if (nameOverride) {
result.push(state.sourceNode(this.callee, nameOverride));
result.push(
//transpile leading comments since we're bypassing callee.transpile (which would normally do this)
...state.transpileLeadingCommentsForAstNode(this),
state.sourceNode(this.callee, nameOverride)
);
} else {
result.push(...this.callee.transpile(state));
}
Expand Down Expand Up @@ -553,6 +557,7 @@ export class DottedGetExpression extends Expression {
//if the callee starts with a namespace name, transpile the name
if (state.file.calleeStartsWithNamespace(this)) {
return [
...state.transpileLeadingCommentsForAstNode(this),
state.sourceNode(this, this.getName(ParseMode.BrightScript))
];
} else {
Expand Down Expand Up @@ -1184,6 +1189,8 @@ export class VariableExpression extends Expression {
//if the callee is the name of a known namespace function
if (namespace && state.file.calleeIsKnownNamespaceFunction(this, namespace.getName(ParseMode.BrighterScript))) {
result.push(
//transpile leading comments since the token isn't being transpiled directly
...state.transpileLeadingCommentsForAstNode(this),
state.sourceNode(this, [
namespace.getName(ParseMode.BrightScript),
'_',
Expand Down Expand Up @@ -1828,7 +1835,8 @@ export class AnnotationExpression extends Expression {
}

transpile(state: BrsTranspileState) {
return [];
//transpile only our leading comments
return state.transpileComments(this.leadingTrivia);
}

walk(visitor: WalkVisitor, options: WalkOptions) {
Expand Down
28 changes: 20 additions & 8 deletions src/parser/Statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class Body extends Statement implements TypedefProvider {
}

transpile(state: BrsTranspileState) {
let result = [] as TranspileResult;
let result: TranspileResult = state.transpileAnnotations(this);
for (let i = 0; i < this.statements.length; i++) {
let statement = this.statements[i];
let previousStatement = this.statements[i - 1];
Expand Down Expand Up @@ -419,7 +419,10 @@ export class ExpressionStatement extends Statement {
public readonly location: Location | undefined;

transpile(state: BrsTranspileState) {
return this.expression.transpile(state);
return [
state.transpileAnnotations(this),
this.expression.transpile(state)
];
}

walk(visitor: WalkVisitor, options: WalkOptions) {
Expand Down Expand Up @@ -542,14 +545,17 @@ export class FunctionStatement extends Statement implements TypedefProvider {
return this.func.leadingTrivia;
}

transpile(state: BrsTranspileState) {
transpile(state: BrsTranspileState): TranspileResult {
//create a fake token using the full transpiled name
let nameToken = {
...this.tokens.name,
text: this.getName(ParseMode.BrightScript)
};

return this.func.transpile(state, nameToken);
return [
...state.transpileAnnotations(this),
...this.func.transpile(state, nameToken)
];
}

getTypedef(state: BrsTranspileState) {
Expand Down Expand Up @@ -1686,6 +1692,7 @@ export class NamespaceStatement extends Statement implements TypedefProvider {
transpile(state: BrsTranspileState) {
//namespaces don't actually have any real content, so just transpile their bodies
return [
state.transpileAnnotations(this),
state.transpileLeadingComments(this.tokens.namespace),
this.body.transpile(state),
state.transpileLeadingComments(this.tokens.endNamespace)
Expand Down Expand Up @@ -2625,11 +2632,12 @@ export class ClassStatement extends Statement implements TypedefProvider {
* This invokes the builder, gets an instance of the class, then invokes the "new" function on that class.
*/
private getTranspiledClassFunction(state: BrsTranspileState) {
let result = [] as TranspileResult;
let result: TranspileResult = state.transpileAnnotations(this);
const constructorFunction = this.getConstructorFunction();
const constructorParams = constructorFunction ? constructorFunction.func.parameters : [];

result.push(
state.transpileLeadingComments(this.tokens.class),
state.sourceNode(this.tokens.class, 'function'),
state.sourceNode(this.tokens.class, ' '),
state.sourceNode(this.tokens.name, this.getName(ParseMode.BrightScript)),
Expand Down Expand Up @@ -3346,8 +3354,9 @@ export class EnumStatement extends Statement implements TypedefProvider {
}

transpile(state: BrsTranspileState) {
//enum declarations do not exist at runtime, so don't transpile anything...
//enum declarations don't exist at runtime, so just transpile comments and trivia
return [
state.transpileAnnotations(this),
state.transpileLeadingComments(this.tokens.enum)
];
}
Expand Down Expand Up @@ -3551,8 +3560,11 @@ export class ConstStatement extends Statement implements TypedefProvider {
}

public transpile(state: BrsTranspileState): TranspileResult {
//const declarations don't exist at runtime, so just transpile empty
return [state.transpileLeadingComments(this.tokens.const)];
//const declarations don't exist at runtime, so just transpile comments and trivia
return [
state.transpileAnnotations(this),
state.transpileLeadingComments(this.tokens.const)
];
}

getTypedef(state: BrsTranspileState): TranspileResult {
Expand Down
20 changes: 17 additions & 3 deletions src/parser/TranspileState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { SourceNode } from 'source-map';
import type { Location } from 'vscode-languageserver';
import type { BsConfig } from '../BsConfig';
import { TokenKind } from '../lexer/TokenKind';
import type { Token } from '../lexer/Token';
import { type Token } from '../lexer/Token';
import type { RangeLike } from '../util';
import { util } from '../util';
import type { TranspileResult } from '../interfaces';


interface TranspileToken {
location?: Location;
text: string;
Expand Down Expand Up @@ -113,6 +112,17 @@ export class TranspileState {
);
}

public transpileLeadingCommentsForAstNode(node: { leadingTrivia?: Token[] }) {
const leadingTrivia = node?.leadingTrivia ?? [];
const leadingCommentsSourceNodes = this.transpileComments(leadingTrivia);
if (leadingCommentsSourceNodes.length > 0) {
// indent in preparation for next text
leadingCommentsSourceNodes.push(this.indent());
}

return leadingCommentsSourceNodes;
}

public transpileLeadingComments(token: TranspileToken) {
const leadingTrivia = (token?.leadingTrivia ?? []);
const leadingCommentsSourceNodes = this.transpileComments(leadingTrivia);
Expand All @@ -124,7 +134,7 @@ export class TranspileState {
return leadingCommentsSourceNodes;
}

public transpileComments(tokens: TranspileToken[]) {
public transpileComments(tokens: TranspileToken[], prepNextLine = false): Array<string | SourceNode> {
const leadingCommentsSourceNodes = [];
const justComments = tokens.filter(t => t.kind === TokenKind.Comment || t.kind === TokenKind.Newline);
let newLinesSinceComment = 0;
Expand All @@ -150,6 +160,10 @@ export class TranspileState {
}
transpiledCommentAlready = true;
}
//if we should prepare for the next line, add an indent (only if applicable)
if (prepNextLine && transpiledCommentAlready) {
leadingCommentsSourceNodes.push(this.indent());
}
return leadingCommentsSourceNodes;
}

Expand Down

0 comments on commit 58ad447

Please sign in to comment.