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

rokucommunity / brighterscript / #12717

14 Jun 2024 08:20PM UTC coverage: 85.629% (-2.3%) from 87.936%
#12717

push

web-flow
Merge 94311dc0a into 42db50190

10808 of 13500 branches covered (80.06%)

Branch coverage included in aggregate %.

6557 of 7163 new or added lines in 96 files covered. (91.54%)

83 existing lines in 17 files now uncovered.

12270 of 13451 relevant lines covered (91.22%)

26531.66 hits per line

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

95.65
/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>
22✔
16
    ) {
17

18
    }
19

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

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

60
        scope.unlinkSymbolTable();
22✔
61

62
        //add all tokens to the event
63
        this.event.semanticTokens.push(
22✔
64
            ...this.result.values()
65
        );
66
    }
67

68
    private result = new Map<string, SemanticToken>();
22✔
69

70

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

92
    private addToken(locatable: Locatable, type: SemanticTokenTypes, modifiers: SemanticTokenModifiers[] = []) {
141✔
93
        //only keep a single token per range. Last-in wins
94
        this.result.set(util.rangeToString(locatable.location.range), {
150✔
95
            range: locatable.location.range,
96
            tokenType: type,
97
            tokenModifiers: modifiers
98
        });
99
    }
100

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

115
            //if this is a function statement or expression, treat it as a function
116
            if (isFunctionExpression(node) || isFunctionStatement(node)) {
34✔
117
                return { type: SemanticTokenTypes.function };
24✔
118
            }
119
            if (
10✔
120
                //if this is a standalone function
121
                isVariableExpression(node) ||
26✔
122
                //if this is a dottedGet, and the LHS is a namespace, treat it as a function.
123
                (isDottedGetExpression(node) && isNamespaceType(node.obj.getType({ flags: SymbolTypeFlag.typetime })))
124
            ) {
125
                return { type: SemanticTokenTypes.function };
8✔
126

127
                //all others should be treated as methods
128
            } else {
129
                return { type: SemanticTokenTypes.method };
2✔
130
            }
131
        } else if (isInterfaceType(type)) {
87✔
132
            return { type: SemanticTokenTypes.interface };
3✔
133
        } else if (isComponentType(type)) {
84!
NEW
134
            return { type: SemanticTokenTypes.class };
×
135
        } else if (isEnumType(type)) {
84✔
136
            return { type: SemanticTokenTypes.enum };
6✔
137
        } else if (isEnumMemberType(type)) {
78✔
138
            return { type: SemanticTokenTypes.enumMember };
2✔
139
        } else if (isNamespaceType(type)) {
76✔
140
            return { type: SemanticTokenTypes.namespace };
57✔
141
            //this is separate from the checks above because we want to resolve alias lookups before turning this variable into a const
142
        } else if (isConstStatement(node)) {
19✔
143
            return { type: SemanticTokenTypes.variable, modifiers: [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static] };
4✔
144
        } else if (isVariableExpression(node)) {
15✔
145
            return { type: SemanticTokenTypes.variable };
14✔
146
        } else {
147
            //we don't know what it is...return undefined to prevent creating a semantic token
148
        }
149
    }
150
}
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