• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brighterscript / #13117

01 Oct 2024 08:24AM UTC coverage: 86.842% (-1.4%) from 88.193%
#13117

push

web-flow
Merge abd960cd5 into 3a2dc7282

11537 of 14048 branches covered (82.13%)

Branch coverage included in aggregate %.

6991 of 7582 new or added lines in 100 files covered. (92.21%)

83 existing lines in 18 files now uncovered.

12692 of 13852 relevant lines covered (91.63%)

29478.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.38
/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts
1
import { SemanticTokenModifiers } from 'vscode-languageserver-protocol';
1✔
2
import { SemanticTokenTypes } from 'vscode-languageserver-protocol';
1✔
3
import { isCallableType, isClassType, isComponentType, isConstStatement, isDottedGetExpression, isEnumMemberType, isEnumType, isFunctionExpression, isFunctionStatement, isInterfaceType, isNamespaceType, isVariableExpression } from '../../astUtils/reflection';
1✔
4
import type { BrsFile } from '../../files/BrsFile';
5
import type { ExtraSymbolData, OnGetSemanticTokensEvent, SemanticToken, TypeChainEntry } from '../../interfaces';
6
import type { Locatable, Token } from '../../lexer/Token';
7
import util from '../../util';
1✔
8
import { SymbolTypeFlag } from '../../SymbolTypeFlag';
1✔
9
import type { BscType } from '../../types/BscType';
10
import { WalkMode, createVisitor } from '../../astUtils/visitors';
1✔
11
import type { AstNode } from '../../parser/AstNode';
12

13
export class BrsFileSemanticTokensProcessor {
1✔
14
    public constructor(
15
        public event: OnGetSemanticTokensEvent<BrsFile>
24✔
16
    ) {
17

18
    }
19

20
    public process() {
21
        const scope = this.event.scopes[0];
24✔
22
        this.result.clear();
24✔
23
        if (scope) {
24!
24
            scope.linkSymbolTable();
24✔
25
        }
26

27
        this.event.file.ast.walk(createVisitor({
24✔
28
            VariableExpression: (node) => {
29
                this.tryAddToken(node, node.tokens.name);
63✔
30
            },
31
            AssignmentStatement: (node) => {
32
                this.addToken(node.tokens.name, SemanticTokenTypes.variable);
8✔
33
            },
34
            DottedGetExpression: (node) => {
35
                this.tryAddToken(node, node.tokens.name);
41✔
36
            },
37
            ConstStatement: (node) => {
38
                this.tryAddToken(node, node.tokens.name);
4✔
39
            },
40
            AliasStatement: (node) => {
41
                this.tryAddToken(node, node.tokens.name);
1✔
42
            },
43
            ClassStatement: (node) => {
44
                this.tryAddToken(node, node.tokens.name);
6✔
45
            },
46
            InterfaceStatement: (node) => {
47
                this.tryAddToken(node, node.tokens.name);
3✔
48
            },
49
            EnumStatement: (node) => {
50
                this.tryAddToken(node, node.tokens.name);
3✔
51
            },
52
            FunctionStatement: (node) => {
53
                this.tryAddToken(node, node.tokens.name);
26✔
54
            },
55
            FunctionParameterExpression: (node) => {
56
                this.addToken(node.tokens.name, SemanticTokenTypes.parameter);
9✔
57
            }
58
        }), {
59
            walkMode: WalkMode.visitAllRecursive
60
        });
61

62
        if (scope) {
24!
63
            scope.unlinkSymbolTable();
24✔
64
        }
65

66
        //add all tokens to the event
67
        this.event.semanticTokens.push(
24✔
68
            ...this.result.values()
69
        );
70
    }
71

72
    private result = new Map<string, SemanticToken>();
24✔
73

74

75
    /**
76
     * Add the given token and node IF we have a resolvable type
77
     */
78
    private tryAddToken(node: AstNode, token: Token) {
79
        const extraData = {} as ExtraSymbolData;
147✔
80
        const chain = [] as TypeChainEntry[];
147✔
81
        // eslint-disable-next-line no-bitwise
82
        const symbolType = node.getType({ flags: SymbolTypeFlag.runtime, data: extraData, typeChain: chain });
147✔
83
        if (symbolType?.isResolvable()) {
147!
84
            let info = this.getSemanticTokenInfo(node, symbolType, extraData);
143✔
85
            if (info) {
143✔
86
                //mark as deprecated if applicable
87
                if ((extraData.flags & SymbolTypeFlag.deprecated)) { // eslint-disable-line no-bitwise
138✔
88
                    info.modifiers ??= [];
1!
89
                    info.modifiers.push(SemanticTokenModifiers.deprecated);
1✔
90
                }
91
                this.addToken(token, info.type, info.modifiers);
138✔
92
            }
93
        }
94
    }
95

96
    private addToken(locatable: Locatable, type: SemanticTokenTypes, modifiers: SemanticTokenModifiers[] = []) {
146✔
97
        //only keep a single token per range. Last-in wins
98
        this.result.set(util.rangeToString(locatable.location.range), {
155✔
99
            range: locatable.location.range,
100
            tokenType: type,
101
            tokenModifiers: modifiers
102
        });
103
    }
104

105
    private getSemanticTokenInfo(node: AstNode, type: BscType, extraData: ExtraSymbolData): { type: SemanticTokenTypes; modifiers?: SemanticTokenModifiers[] } {
106
        if (isConstStatement(extraData?.definingNode)) {
143!
107
            return { type: SemanticTokenTypes.variable, modifiers: [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static] };
4✔
108
            // non-instances of classes should be colored like classes
109
        } else if (isClassType(type) && extraData.isInstance !== true) {
139✔
110
            return { type: SemanticTokenTypes.class };
10✔
111
            //function statements and expressions
112
        } else if (isCallableType(type)) {
129✔
113
            //if the typetime type is a class, then color this like a class
114
            const typetimeType = node.getType({ flags: SymbolTypeFlag.typetime });
38✔
115
            if (isClassType(typetimeType)) {
38✔
116
                return { type: SemanticTokenTypes.class };
1✔
117
            }
118

119
            //if this is a function statement or expression, treat it as a function
120
            if (isFunctionExpression(node) || isFunctionStatement(node)) {
37✔
121
                return { type: SemanticTokenTypes.function };
26✔
122
            }
123
            if (
11✔
124
                //if this is a standalone function
125
                isVariableExpression(node) ||
27✔
126
                //if this is a dottedGet, and the LHS is a namespace, treat it as a function.
127
                (isDottedGetExpression(node) && isNamespaceType(node.obj.getType({ flags: SymbolTypeFlag.typetime })))
128
            ) {
129
                return { type: SemanticTokenTypes.function };
9✔
130

131
                //all others should be treated as methods
132
            } else {
133
                return { type: SemanticTokenTypes.method };
2✔
134
            }
135
        } else if (isInterfaceType(type) && extraData.isInstance !== true) {
91✔
136
            return { type: SemanticTokenTypes.interface };
3✔
137
        } else if (isComponentType(type) && extraData.isInstance !== true) {
88!
NEW
138
            return { type: SemanticTokenTypes.class };
×
139
        } else if (isEnumType(type)) {
88✔
140
            return { type: SemanticTokenTypes.enum };
6✔
141
        } else if (isEnumMemberType(type)) {
82✔
142
            return { type: SemanticTokenTypes.enumMember };
2✔
143
        } else if (isNamespaceType(type)) {
80✔
144
            return { type: SemanticTokenTypes.namespace };
57✔
145
            //this is separate from the checks above because we want to resolve alias lookups before turning this variable into a const
146
        } else if (isConstStatement(node)) {
23✔
147
            return { type: SemanticTokenTypes.variable, modifiers: [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static] };
4✔
148
        } else if (isVariableExpression(node)) {
19✔
149
            if (/m/i.test(node.tokens?.name?.text)) {
18!
150
                //don't color `m` variables
151
            } else {
152
                return { type: SemanticTokenTypes.variable };
14✔
153
            }
154
        } else {
155
            //we don't know what it is...return undefined to prevent creating a semantic token
156
        }
157
    }
158
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc