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

rokucommunity / brighterscript / #13234

28 Oct 2024 07:06PM UTC coverage: 89.066% (+2.2%) from 86.866%
#13234

push

web-flow
Merge 9bcb77aad into 9ec6f722c

7233 of 8558 branches covered (84.52%)

Branch coverage included in aggregate %.

34 of 34 new or added lines in 5 files covered. (100.0%)

543 existing lines in 53 files now uncovered.

9621 of 10365 relevant lines covered (92.82%)

1782.52 hits per line

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

70.09
/src/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.ts
1
import type { Range } from 'vscode-languageserver-protocol';
2
import { SemanticTokenModifiers } from 'vscode-languageserver-protocol';
1✔
3
import { SemanticTokenTypes } from 'vscode-languageserver-protocol';
1✔
4
import { isCallExpression, isCustomType, isNamespaceStatement, isNewExpression } from '../../astUtils/reflection';
1✔
5
import type { BrsFile } from '../../files/BrsFile';
6
import type { OnGetSemanticTokensEvent } from '../../interfaces';
7
import type { Locatable } from '../../lexer/Token';
8
import { ParseMode } from '../../parser/Parser';
1✔
9
import type { NamespaceStatement } from '../../parser/Statement';
10
import util from '../../util';
1✔
11

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

17
    }
18

19
    public process() {
20
        this.handleClasses();
16✔
21
        this.handleConstDeclarations();
16✔
22
        this.iterateNodes();
16✔
23
    }
24

25
    private handleConstDeclarations() {
26
        for (const stmt of this.event.file.parser.references.constStatements) {
16✔
27
            this.addToken(stmt.tokens.name, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static]);
4✔
28
        }
29
    }
30

31
    private handleClasses() {
32

33
        const classes = [] as Array<{ className: string; namespaceName: string; range: Range }>;
16✔
34

35
        //classes used in function param types
36
        for (const func of this.event.file.parser.references.functionExpressions) {
16✔
37
            for (const parm of func.parameters) {
21✔
38
                if (isCustomType(parm.type)) {
8!
UNCOV
39
                    const namespace = parm.findAncestor<NamespaceStatement>(isNamespaceStatement);
×
UNCOV
40
                    classes.push({
×
41
                        className: parm.typeToken.text,
42
                        namespaceName: namespace?.getName(ParseMode.BrighterScript),
×
43
                        range: parm.typeToken.range
44
                    });
45
                }
46
            }
47
        }
48

49
        for (const cls of classes) {
16✔
UNCOV
50
            if (
×
51
                cls.className.length > 0 &&
×
52
                //only highlight classes that are in scope
UNCOV
53
                this.event.scopes.some(x => x.hasClass(cls.className, cls.namespaceName))
×
54
            ) {
UNCOV
55
                const tokens = util.splitGetRange('.', cls.className, cls.range);
×
UNCOV
56
                this.addTokens(tokens.reverse(), SemanticTokenTypes.class, SemanticTokenTypes.namespace);
×
57
            }
58
        }
59
    }
60

61
    /**
62
     * Add tokens for each locatable item in the list.
63
     * Each locatable is paired with a token type. If there are more locatables than token types, all remaining locatables are given the final token type
64
     */
65
    private addTokens(locatables: Locatable[], ...semanticTokenTypes: SemanticTokenTypes[]) {
UNCOV
66
        for (let i = 0; i < locatables.length; i++) {
×
UNCOV
67
            const locatable = locatables[i];
×
68
            //skip items that don't have a location
UNCOV
69
            if (locatable?.range) {
×
UNCOV
70
                this.addToken(
×
71
                    locatables[i],
72
                    //use the type at the index, or the last type if missing
73
                    semanticTokenTypes[i] ?? semanticTokenTypes[semanticTokenTypes.length - 1]
×
74
                );
75
            }
76
        }
77
    }
78

79
    private addToken(locatable: Locatable, type: SemanticTokenTypes, modifiers: SemanticTokenModifiers[] = []) {
40✔
80
        this.event.semanticTokens.push({
48✔
81
            range: locatable.range,
82
            tokenType: type,
83
            tokenModifiers: modifiers
84
        });
85
    }
86

87
    private iterateNodes() {
88
        const scope = this.event.scopes[0];
16✔
89

90
        //if this file has no scopes, there's nothing else we can do about this
91
        if (!scope) {
16!
UNCOV
92
            return;
×
93
        }
94

95
        const nodes = [
16✔
96
            ...this.event.file.parser.references.expressions,
97
            //make a new VariableExpression to wrap the name. This is a hack, we could probably do it better
98
            ...this.event.file.parser.references.assignmentStatements,
99
            ...this.event.file.parser.references.functionExpressions.map(x => x.parameters).flat()
21✔
100
        ];
101

102
        for (let node of nodes) {
16✔
103
            //lift the callee from call expressions to handle namespaced function calls
104
            if (isCallExpression(node)) {
56✔
105
                node = node.callee;
3✔
106
            } else if (isNewExpression(node)) {
53✔
107
                node = node.call.callee;
2✔
108
            }
109
            const containingNamespaceNameLower = node.findAncestor<NamespaceStatement>(isNamespaceStatement)?.getName(ParseMode.BrighterScript).toLowerCase();
56✔
110
            const tokens = util.getAllDottedGetParts(node);
56✔
111
            const processedNames: string[] = [];
56✔
112
            for (const token of tokens ?? []) {
56✔
113
                processedNames.push(token.text?.toLowerCase());
62!
114
                const entityName = processedNames.join('.');
62✔
115

116
                if (scope.getEnumMemberFileLink(entityName, containingNamespaceNameLower)) {
62✔
117
                    this.addToken(token, SemanticTokenTypes.enumMember);
3✔
118
                } else if (scope.getEnum(entityName, containingNamespaceNameLower)) {
59✔
119
                    this.addToken(token, SemanticTokenTypes.enum);
4✔
120
                } else if (scope.getClass(entityName, containingNamespaceNameLower)) {
55✔
121
                    this.addToken(token, SemanticTokenTypes.class);
2✔
122
                } else if (scope.getCallableByName(entityName)) {
53✔
123
                    this.addToken(token, SemanticTokenTypes.function);
6✔
124
                } else if (scope.getNamespace(entityName, containingNamespaceNameLower)) {
47✔
125
                    this.addToken(token, SemanticTokenTypes.namespace);
25✔
126
                } else if (scope.getConstFileLink(entityName, containingNamespaceNameLower)) {
22✔
127
                    this.addToken(token, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static]);
4✔
128
                }
129
            }
130
        }
131
    }
132
}
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