• 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

81.69
/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 { XmlFile } from '../../files/XmlFile';
8
import type { BscFile, OnGetCodeActionsEvent } from '../../interfaces';
9
import { ParseMode } from '../../parser/Parser';
1✔
10
import { util } from '../../util';
1✔
11

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

17
    }
18

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

31
    private suggestedImports = new Set<string>();
10✔
32

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

42
        this.suggestedImports.add(key);
7✔
43
        const importStatements = (this.event.file as BrsFile).parser.references.importStatements;
7✔
44
        //find the position of the first import statement, or the top of the file if there is none
45
        const insertPosition = importStatements[importStatements.length - 1]?.importToken.range?.start ?? util.createPosition(0, 0);
7✔
46

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

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

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

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

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