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

rokucommunity / brighterscript / #13978

10 Mar 2025 01:00PM UTC coverage: 86.759% (-2.4%) from 89.113%
#13978

push

web-flow
Merge 96004e807 into 700183e7d

12688 of 15459 branches covered (82.08%)

Branch coverage included in aggregate %.

28 of 28 new or added lines in 1 file covered. (100.0%)

924 existing lines in 53 files now uncovered.

13614 of 14857 relevant lines covered (91.63%)

19945.24 hits per line

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

84.95
/src/diagnosticUtils.ts
1
import type { Chalk } from 'chalk';
2
import chalk from 'chalk';
1✔
3
import type { BsConfig } from './BsConfig';
4
import { DiagnosticSeverity } from 'vscode-languageserver';
1✔
5
import type { BsDiagnostic } from './interfaces';
6
import type { Range } from 'vscode-languageserver';
7

8
export const MAX_RELATED_INFOS_COUNT = 3;
1✔
9

10
/**
11
 * Prepare print diagnostic formatting options
12
 */
13
export function getPrintDiagnosticOptions(options: BsConfig) {
1✔
14
    let cwd = options?.cwd ? options.cwd : process.cwd();
57!
15

16
    let emitFullPaths = options?.emitFullPaths === true;
57!
17

18
    let diagnosticLevel = options?.diagnosticLevel ?? 'warn';
57!
19

20
    let diagnosticSeverityMap = {} as Record<string, DiagnosticSeverity>;
57✔
21
    diagnosticSeverityMap.info = DiagnosticSeverity.Information;
57✔
22
    diagnosticSeverityMap.hint = DiagnosticSeverity.Hint;
57✔
23
    diagnosticSeverityMap.warn = DiagnosticSeverity.Warning;
57✔
24
    diagnosticSeverityMap.error = DiagnosticSeverity.Error;
57✔
25

26
    let severityLevel = diagnosticSeverityMap[diagnosticLevel] || DiagnosticSeverity.Warning;
57✔
27
    let order = [DiagnosticSeverity.Information, DiagnosticSeverity.Hint, DiagnosticSeverity.Warning, DiagnosticSeverity.Error];
57✔
28
    let includeDiagnostic = order.slice(order.indexOf(severityLevel)).reduce((acc, value) => {
57✔
29
        acc[value] = true;
118✔
30
        return acc;
118✔
31
    }, {});
32

33
    let typeColor = {} as Record<string, Chalk>;
57✔
34
    typeColor[DiagnosticSeverity.Information] = chalk.blue;
57✔
35
    typeColor[DiagnosticSeverity.Hint] = chalk.green;
57✔
36
    typeColor[DiagnosticSeverity.Warning] = chalk.yellow;
57✔
37
    typeColor[DiagnosticSeverity.Error] = chalk.red;
57✔
38

39
    let severityTextMap = {};
57✔
40
    severityTextMap[DiagnosticSeverity.Information] = 'info';
57✔
41
    severityTextMap[DiagnosticSeverity.Hint] = 'hint';
57✔
42
    severityTextMap[DiagnosticSeverity.Warning] = 'warning';
57✔
43
    severityTextMap[DiagnosticSeverity.Error] = 'error';
57✔
44

45
    return {
57✔
46
        cwd: cwd,
47
        emitFullPaths: emitFullPaths,
48
        severityLevel: severityLevel,
49
        includeDiagnostic: includeDiagnostic,
50
        typeColor: typeColor,
51
        severityTextMap: severityTextMap
52
    };
53
}
54

55
/**
56
 * Format output of one diagnostic
57
 */
58
export function printDiagnostic(
1✔
59
    options: ReturnType<typeof getPrintDiagnosticOptions>,
60
    severity: DiagnosticSeverity,
61
    filePath: string | undefined,
62
    lines: string[],
63
    diagnostic: BsDiagnostic,
64
    relatedInformation?: Array<{ range: Range; filePath: string; message: string }>
65
) {
66
    let { includeDiagnostic, severityTextMap, typeColor } = options;
8✔
67

68
    if (!includeDiagnostic[severity]) {
8!
UNCOV
69
        return;
×
70
    }
71

72
    let severityText = severityTextMap[severity];
8✔
73

74
    console.log('');
8✔
75
    const printableDiagnosticCode = diagnostic.code ? diagnostic.code.toString() : 'BS' + ((diagnostic as BsDiagnostic).legacyCode ?? '');
8!
76

77
    console.log(
8✔
78
        chalk.cyan(filePath ?? '<unknown file>') +
24✔
79
        ':' +
80
        chalk.yellow(
81
            diagnostic.location?.range
22✔
82
                ? (diagnostic.location.range.start.line + 1) + ':' + (diagnostic.location.range.start.character + 1)
8✔
83
                : 'line?:col?'
84
        ) +
85
        ' - ' +
86
        typeColor[severity](severityText) +
87
        ' ' +
88
        chalk.grey(printableDiagnosticCode) +
89
        ': ' +
90
        chalk.white(diagnostic.message)
91
    );
92
    console.log('');
8✔
93

94
    //Get the line referenced by the diagnostic. if we couldn't find a line,
95
    // default to an empty string so it doesn't crash the error printing below
96
    let diagnosticLine = lines[diagnostic.location?.range?.start?.line ?? -1] ?? '';
8!
97
    console.log(
8✔
98
        getDiagnosticLine(diagnostic, diagnosticLine, typeColor[severity])
99
    );
100

101
    //print related information if present (only first few rows)
102
    const relatedInfoList = relatedInformation ?? [];
8✔
103
    let indent = '    ';
8✔
104
    for (let i = 0; i < relatedInfoList.length; i++) {
8✔
105
        let relatedInfo = relatedInfoList[i];
×
106
        //only show the first MAX_RELATED_INFOS_COUNT relatedInfo links
UNCOV
107
        if (i < MAX_RELATED_INFOS_COUNT) {
×
UNCOV
108
            console.log('');
×
UNCOV
109
            console.log(
×
110
                indent,
111
                chalk.cyan(relatedInfo.filePath ?? '<unknown file>') +
×
112
                ':' +
113
                chalk.yellow(
114
                    relatedInfo.range
115
                        ? (relatedInfo.range.start.line + 1) + ':' + (relatedInfo.range.start.character + 1)
×
116
                        : 'line?:col?'
117
                )
118
            );
UNCOV
119
            console.log(indent, relatedInfo.message);
×
120
        } else {
UNCOV
121
            console.log('\n', indent, `...and ${relatedInfoList.length - i + 1} more`);
×
UNCOV
122
            break;
×
123
        }
124
    }
125
    console.log('');
8✔
126
}
127

128
export function getDiagnosticLine(diagnostic: BsDiagnostic, diagnosticLine: string, colorFunction: Chalk) {
1✔
129
    let result = '';
14✔
130

131
    //only print the line information if we have some
132
    if (diagnostic.location?.range && diagnosticLine) {
14✔
133
        const lineNumberText = chalk.bgWhite(' ' + chalk.black((diagnostic.location.range.start.line + 1).toString()) + ' ') + ' ';
6✔
134
        const blankLineNumberText = chalk.bgWhite(' ' + chalk.white(' '.repeat((diagnostic.location.range.start.line + 1).toString().length)) + ' ') + ' ';
6✔
135

136
        //remove tabs in favor of spaces to make diagnostic printing more consistent
137
        let leadingText = diagnosticLine.slice(0, diagnostic.location.range.start.character);
6✔
138
        let leadingTextNormalized = leadingText.replace(/\t/g, '    ');
6✔
139
        let actualText = diagnosticLine.slice(diagnostic.location.range.start.character, diagnostic.location.range.end.character);
6✔
140
        let actualTextNormalized = actualText.replace(/\t/g, '    ');
6✔
141
        let startIndex = leadingTextNormalized.length;
6✔
142
        let endIndex = leadingTextNormalized.length + actualTextNormalized.length;
6✔
143

144
        let diagnosticLineNormalized = diagnosticLine.replace(/\t/g, '    ');
6✔
145

146
        const squigglyText = getDiagnosticSquigglyText(diagnosticLineNormalized, startIndex, endIndex);
6✔
147
        result +=
6✔
148
            lineNumberText + diagnosticLineNormalized + '\n' +
149
            blankLineNumberText + colorFunction(squigglyText);
150
    }
151
    return result;
14✔
152
}
153

154
/**
155
 * Given a diagnostic, compute the range for the squiggly
156
 */
157
export function getDiagnosticSquigglyText(line: string | undefined, startCharacter: number | undefined, endCharacter: number | undefined) {
1✔
158
    let squiggle: string;
159
    //fill the entire line
160
    if (
18✔
161
        //there is no range
162
        typeof startCharacter !== 'number' || typeof endCharacter !== 'number' ||
81✔
163
        //there is no line
164
        !line ||
165
        //both positions point to same location
166
        startCharacter === endCharacter ||
167
        //the diagnostic starts after the end of the line
168
        startCharacter >= line.length
169
    ) {
170
        squiggle = ''.padStart(line?.length ?? 0, '~');
4✔
171
    } else {
172

173
        let endIndex = Math.max(endCharacter, line.length);
14✔
174
        endIndex = endIndex > 0 ? endIndex : 0;
14!
175
        if (line?.length < endIndex) {
14!
176
            endIndex = line.length;
3✔
177
        }
178

179
        let leadingWhitespaceLength = startCharacter;
14✔
180
        let squiggleLength: number;
181
        if (endCharacter === Number.MAX_VALUE) {
14✔
182
            squiggleLength = line.length - leadingWhitespaceLength;
1✔
183
        } else {
184
            squiggleLength = endCharacter - startCharacter;
13✔
185
        }
186
        let trailingWhitespaceLength = endIndex - endCharacter;
14✔
187

188
        //opening whitespace
189
        squiggle =
14✔
190
            ''.padStart(leadingWhitespaceLength, ' ') +
191
            //squiggle
192
            ''.padStart(squiggleLength, '~') +
193
            //trailing whitespace
194
            ''.padStart(trailingWhitespaceLength, ' ');
195

196
        //trim the end of the squiggle so it doesn't go longer than the end of the line
197
        if (squiggle.length > endIndex) {
14✔
198
            squiggle = squiggle.slice(0, endIndex);
2✔
199
        }
200
    }
201
    return squiggle;
18✔
202
}
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