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

rokucommunity / brighterscript / #12854

25 Jul 2024 05:41PM UTC coverage: 85.626% (-0.6%) from 86.202%
#12854

push

web-flow
Merge 7c29dfd7b into 5f3ffa3fa

10816 of 13510 branches covered (80.06%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 2 files covered. (100.0%)

318 existing lines in 19 files now uncovered.

12279 of 13462 relevant lines covered (91.21%)

26654.75 hits per line

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

84.44
/src/bscPlugin/codeActions/CodeActionsProcessor.ts
1
import type { Diagnostic } from 'vscode-languageserver';
2
import { CodeActionKind } from 'vscode-languageserver';
1✔
3
import { codeActionUtil } from '../../CodeActionUtil';
1✔
4
import type { DiagnosticMessageType } from '../../DiagnosticMessages';
5
import { DiagnosticCodeMap } from '../../DiagnosticMessages';
1✔
6
import type { BrsFile } from '../../files/BrsFile';
7
import type { BscFile } from '../../files/BscFile';
8
import type { XmlFile } from '../../files/XmlFile';
9
import type { OnGetCodeActionsEvent } from '../../interfaces';
10
import { ParseMode } from '../../parser/Parser';
1✔
11
import { util } from '../../util';
1✔
12
import { isBrsFile } from '../../astUtils/reflection';
1✔
13

14
export class CodeActionsProcessor {
1✔
15
    public constructor(
16
        public event: OnGetCodeActionsEvent
10✔
17
    ) {
18

19
    }
20

21
    public process() {
22
        for (const diagnostic of this.event.diagnostics) {
10✔
23
            if (diagnostic.code === DiagnosticCodeMap.cannotFindName || diagnostic.code === DiagnosticCodeMap.cannotFindFunction) {
12✔
24
                this.suggestCannotFindName(diagnostic as any);
8✔
25
            } else if (diagnostic.code === DiagnosticCodeMap.classCouldNotBeFound) {
4!
UNCOV
26
                this.suggestClassImports(diagnostic as any);
×
27
            } else if (diagnostic.code === DiagnosticCodeMap.xmlComponentMissingExtendsAttribute) {
4✔
28
                this.addMissingExtends(diagnostic as any);
2✔
29
            }
30
        }
31
    }
32

33
    private suggestedImports = new Set<string>();
10✔
34

35
    /**
36
     * Generic import suggestion function. Shouldn't be called directly from the main loop, but instead called by more specific diagnostic handlers
37
     */
38
    private suggestImports(diagnostic: Diagnostic, key: string, files: BscFile[]) {
39
        //skip if we already have this suggestion
40
        if (this.suggestedImports.has(key) || !isBrsFile(this.event.file)) {
7!
UNCOV
41
            return;
×
42
        }
43

44
        this.suggestedImports.add(key);
7✔
45
        // eslint-disable-next-line @typescript-eslint/dot-notation
46
        const importStatements = this.event.file['_cachedLookups'].importStatements;
7✔
47
        //find the position of the first import statement, or the top of the file if there is none
48
        const insertPosition = importStatements[importStatements.length - 1]?.tokens.import?.location?.range?.start ?? util.createPosition(0, 0);
7✔
49

50
        //find all files that reference this function
51
        for (const file of files) {
7✔
52
            const destPath = util.sanitizePkgPath(file.destPath);
9✔
53
            this.event.codeActions.push(
9✔
54
                codeActionUtil.createCodeAction({
55
                    title: `import "${destPath}"`,
56
                    diagnostics: [diagnostic],
57
                    isPreferred: false,
58
                    kind: CodeActionKind.QuickFix,
59
                    changes: [{
60
                        type: 'insert',
61
                        filePath: this.event.file.srcPath,
62
                        position: insertPosition,
63
                        newText: `import "${destPath}"\n`
64
                    }]
65
                })
66
            );
67
        }
68
    }
69

70
    private suggestCannotFindName(diagnostic: DiagnosticMessageType<'cannotFindName'>) {
71
        //skip if not a BrighterScript file
72
        if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) {
8✔
73
            return;
1✔
74
        }
75
        const lowerName = (diagnostic.data.fullName ?? diagnostic.data.name).toLowerCase();
7!
76

77
        this.suggestImports(
7✔
78
            diagnostic,
79
            lowerName,
80
            [
81
                ...this.event.program.findFilesForFunction(lowerName),
82
                ...this.event.program.findFilesForClass(lowerName),
83
                ...this.event.program.findFilesForNamespace(lowerName),
84
                ...this.event.program.findFilesForEnum(lowerName)
85
            ]
86
        );
87
    }
88

89
    private suggestClassImports(diagnostic: DiagnosticMessageType<'classCouldNotBeFound'>) {
90
        //skip if not a BrighterScript file
91
        if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) {
×
92
            return;
×
93
        }
UNCOV
94
        const lowerClassName = diagnostic.data.className.toLowerCase();
×
95
        this.suggestImports(
×
96
            diagnostic,
97
            lowerClassName,
98
            this.event.program.findFilesForClass(lowerClassName)
99
        );
100
    }
101

102
    private addMissingExtends(diagnostic: DiagnosticMessageType<'xmlComponentMissingExtendsAttribute'>) {
103
        const srcPath = this.event.file.srcPath;
2✔
104
        const { componentElement } = (this.event.file as XmlFile).parser.ast;
2✔
105
        //inject new attribute after the final attribute, or after the `<component` if there are no attributes
106
        const pos = (componentElement.attributes[componentElement.attributes.length - 1] ?? componentElement.tokens.startTagName)?.location?.range.end;
2!
107
        this.event.codeActions.push(
2✔
108
            codeActionUtil.createCodeAction({
109
                title: `Extend "Group"`,
110
                diagnostics: [diagnostic],
111
                isPreferred: true,
112
                kind: CodeActionKind.QuickFix,
113
                changes: [{
114
                    type: 'insert',
115
                    filePath: srcPath,
116
                    position: pos,
117
                    newText: ' extends="Group"'
118
                }]
119
            })
120
        );
121
        this.event.codeActions.push(
2✔
122
            codeActionUtil.createCodeAction({
123
                title: `Extend "Task"`,
124
                diagnostics: [diagnostic],
125
                kind: CodeActionKind.QuickFix,
126
                changes: [{
127
                    type: 'insert',
128
                    filePath: srcPath,
129
                    position: pos,
130
                    newText: ' extends="Task"'
131
                }]
132
            })
133
        );
134
        this.event.codeActions.push(
2✔
135
            codeActionUtil.createCodeAction({
136
                title: `Extend "ContentNode"`,
137
                diagnostics: [diagnostic],
138
                kind: CodeActionKind.QuickFix,
139
                changes: [{
140
                    type: 'insert',
141
                    filePath: srcPath,
142
                    position: pos,
143
                    newText: ' extends="ContentNode"'
144
                }]
145
            })
146
        );
147
    }
148
}
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