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

rokucommunity / brighterscript / #9668

pending completion
#9668

push

web-flow
Let users use or discard explicit types (#744)

* Add a useExplicitTypes configuration option

* Prevent writing the 'as type' when useExplicitType is false

* Add documentation for the useExplicitTypes config

* Rename option to removeParameterTypes

Co-authored-by: Bronley Plumb <bronley@gmail.com>

5558 of 6810 branches covered (81.62%)

Branch coverage included in aggregate %.

8316 of 8996 relevant lines covered (92.44%)

1580.74 hits per line

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

68.0
/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>
13✔
15
    ) {
16

17
    }
18

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

25
    private handleConstDeclarations() {
26
        for (const stmt of this.event.file.parser.references.constStatements) {
13✔
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 }>;
13✔
34

35
        //classes used in function param types
36
        for (const func of this.event.file.parser.references.functionExpressions) {
13✔
37
            for (const parm of func.parameters) {
18✔
38
                if (isCustomType(parm.type)) {
5!
39
                    const namespace = parm.findAncestor<NamespaceStatement>(isNamespaceStatement);
×
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) {
13✔
50
            if (
×
51
                cls.className.length > 0 &&
×
52
                //only highlight classes that are in scope
53
                this.event.scopes.some(x => x.hasClass(cls.className, cls.namespaceName))
×
54
            ) {
55
                const tokens = util.splitGetRange('.', cls.className, cls.range);
×
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[]) {
66
        for (let i = 0; i < locatables.length; i++) {
×
67
            const locatable = locatables[i];
×
68
            //skip items that don't have a location
69
            if (locatable?.range) {
×
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[] = []) {
36✔
80
        this.event.semanticTokens.push({
44✔
81
            range: locatable.range,
82
            tokenType: type,
83
            tokenModifiers: modifiers
84
        });
85
    }
86

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

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

95
        for (let expression of this.event.file.parser.references.expressions) {
13✔
96
            //lift the callee from call expressions to handle namespaced function calls
97
            if (isCallExpression(expression)) {
37✔
98
                expression = expression.callee;
3✔
99
            } else if (isNewExpression(expression)) {
34✔
100
                expression = expression.call.callee;
2✔
101
            }
102
            const tokens = util.getAllDottedGetParts(expression);
37✔
103
            const processedNames: string[] = [];
37✔
104
            for (const token of tokens ?? []) {
37✔
105
                processedNames.push(token.text?.toLowerCase());
45!
106
                const entityName = processedNames.join('.');
45✔
107

108
                if (scope.getEnumMemberMap().has(entityName)) {
45✔
109
                    this.addToken(token, SemanticTokenTypes.enumMember);
3✔
110
                } else if (scope.getEnumMap().has(entityName)) {
42✔
111
                    this.addToken(token, SemanticTokenTypes.enum);
4✔
112
                } else if (scope.getClassMap().has(entityName)) {
38✔
113
                    this.addToken(token, SemanticTokenTypes.class);
2✔
114
                } else if (scope.getCallableByName(entityName)) {
36✔
115
                    this.addToken(token, SemanticTokenTypes.function);
6✔
116
                } else if (scope.namespaceLookup.has(entityName)) {
30✔
117
                    this.addToken(token, SemanticTokenTypes.namespace);
21✔
118
                } else if (scope.getConstFileLink(entityName)) {
9✔
119
                    this.addToken(token, SemanticTokenTypes.variable, [SemanticTokenModifiers.readonly, SemanticTokenModifiers.static]);
4✔
120
                }
121
            }
122
        }
123
    }
124
}
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

© 2025 Coveralls, Inc