Skip to content

Commit 6ef52db

Browse files
Merge pull request #47 from gjsjohnmurray:fix-46
Fix coverage marking when `"objectscript.multilineMethodArgs": true`
2 parents 0f23606 + 32b9d2c commit 6ef52db

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 2.0.2 (dd-Mmm-2025)
2+
* Fix coverage marking when `"objectscript.multilineMethodArgs": true` (#46)
3+
14
## 2.0.1 (28-Jul-2025)
25
* Activate the Covering Tests filter in the editor's Test Coverage Toolbar (#44)
36
* Various bugfixes and improvements.

src/coverage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export async function getFileCoverageResults(folderUri: vscode.Uri, namespace: s
5656
}
5757
}
5858

59+
const className = element.Name;
60+
5961
// Respect exportSettings.map which the IPM project uses to export %IPM.Foo.cls into IPM/Foo.cls
6062
if (exportSettings.map) {
6163
for (const pattern of Object.keys(exportSettings.map)) {
@@ -68,6 +70,7 @@ export async function getFileCoverageResults(folderUri: vscode.Uri, namespace: s
6870
const fileUri = folderUri.with({ path: folderUri.path.concat(pathPrefix, `/${element.Name.replace(/\./g, '/')}.${fileType}`) });
6971
fileCoverage = new OurFileCoverage(
7072
coverageIndex,
73+
className,
7174
element.Hash,
7275
fileUri,
7376
new vscode.TestCoverageCount(element.CoveredLines, element.ExecutableLines),

src/ourFileCoverage.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { SQL_FN_INT8BITSTRING } from './utils';
77

88
export class OurFileCoverage extends vscode.FileCoverage {
99

10+
public readonly name: string;
1011
public readonly codeUnit: string;
1112
private coverageIndex: number;
1213

13-
constructor(coverageIndex: number, codeUnit: string, uri: vscode.Uri, statementCoverage: vscode.TestCoverageCount, branchCoverage?: vscode.TestCoverageCount, declarationCoverage?: vscode.TestCoverageCount, includesTests?: vscode.TestItem[]) {
14+
constructor(coverageIndex: number, name: string, codeUnit: string, uri: vscode.Uri, statementCoverage: vscode.TestCoverageCount, branchCoverage?: vscode.TestCoverageCount, declarationCoverage?: vscode.TestCoverageCount, includesTests?: vscode.TestItem[]) {
1415
super(uri, statementCoverage, branchCoverage, declarationCoverage, includesTests);
1516
this.coverageIndex = coverageIndex;
17+
this.name = name;
1618
this.codeUnit = codeUnit;
1719
}
1820

@@ -33,9 +35,30 @@ export class OurFileCoverage extends vscode.FileCoverage {
3335
};
3436
const namespace: string = server.namespace.toUpperCase();
3537

38+
// When ObjectScript extension spreads method arguments over multiple lines, we need to compute offsets
39+
const mapOffsets: Map<string, number> = new Map();
40+
if (vscode.workspace.getConfiguration('objectscript', this.uri).get('multilineMethodArgs', false)) {
41+
const response = await makeRESTRequest(
42+
"POST",
43+
serverSpec,
44+
{ apiVersion: 1, namespace, path: "/action/query" },
45+
{
46+
query: "SELECT Name as Method, SUM( CASE WHEN $LENGTH(FormalSpec, ',') > 1 THEN $LENGTH(FormalSpec, ',') ELSE 0 END ) OVER ( ORDER BY SequenceNumber ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS Offset FROM %Dictionary.MethodDefinition WHERE parent = ?",
47+
parameters: [this.name],
48+
},
49+
);
50+
if (response) {
51+
response?.data?.result?.content?.forEach(element => {
52+
const methodName = element.Method;
53+
const offset = Number(element.Offset);
54+
mapOffsets.set(methodName, offset);
55+
});
56+
}
57+
}
58+
3659
// Get map of lines to methods
37-
const mapMethods: Map<number, string> = new Map();
38-
const lineToMethod: string[] = [];
60+
const mapMethodsInCoverage: Map<number, string> = new Map();
61+
const mapMethodsInDocument: Map<number, string> = new Map();
3962
let response = await makeRESTRequest(
4063
"POST",
4164
serverSpec,
@@ -46,14 +69,10 @@ export class OurFileCoverage extends vscode.FileCoverage {
4669
},
4770
);
4871
if (response) {
49-
let previousMethod = "";
50-
let previousLine = 0;
5172
response?.data?.result?.content?.forEach(element => {
5273
const thisLine = Number(element.Line);
53-
mapMethods.set(Number(element.Line), element.Method);
54-
lineToMethod.fill(previousMethod, previousLine, thisLine -1);
55-
previousMethod = element.Method;
56-
previousLine = thisLine;
74+
mapMethodsInCoverage.set(thisLine, element.Method);
75+
mapMethodsInDocument.set(thisLine + (mapOffsets.get(element.Method) || 0), element.Method);
5776
});
5877
}
5978

@@ -80,14 +99,24 @@ export class OurFileCoverage extends vscode.FileCoverage {
8099
// Process the Uint8Bitstring values for executable and covered lines
81100
const i8bsExecutableLines = element.i8bsExecutableLines;
82101
const i8bsCoveredLines = element.i8bsCoveredLines;
102+
103+
let offset = 0; // We will add this to line number in coverage results to get line number in document, adjusted for multiline method arguments
83104
for (let lineChunk = 0; lineChunk < i8bsExecutableLines.length; lineChunk++) {
84105
const executableLines = i8bsExecutableLines.charCodeAt(lineChunk);
85106
const coveredLines = i8bsCoveredLines.charCodeAt(lineChunk);
86107
for (let bitIndex = 0; bitIndex < 8; bitIndex++) {
108+
const lineNumberOfCoverage = lineChunk * 8 + bitIndex + 1;
109+
110+
// On a method declaration line we should recompute the offset
111+
const method = mapMethodsInCoverage.get(lineNumberOfCoverage);
112+
if (method) {
113+
offset = (mapOffsets.get(method) || offset);
114+
}
115+
87116
if ((executableLines & (1 << bitIndex)) !== 0) {
88-
const lineNumber = lineChunk * 8 + bitIndex + 1;
89117
const isCovered = (coveredLines & (1 << bitIndex)) !== 0;
90-
const range = new vscode.Range(new vscode.Position(lineNumber - 1, 0), new vscode.Position(lineNumber - 1, Number.MAX_VALUE));
118+
const lineNumberOfDocument = lineNumberOfCoverage + offset;
119+
const range = new vscode.Range(new vscode.Position(lineNumberOfDocument - 1, 0), new vscode.Position(lineNumberOfDocument - 1, Number.MAX_VALUE));
91120
const statementCoverage = new vscode.StatementCoverage(isCovered, range);
92121
detailedCoverage.push(statementCoverage);
93122
}
@@ -109,18 +138,26 @@ export class OurFileCoverage extends vscode.FileCoverage {
109138
if (response) {
110139
let previousMethod = "";
111140
let previousStartLine = 0;
141+
let startOffset = 0;
142+
let endOffset = 0;
112143
response?.data?.result?.content?.forEach(element => {
144+
const currentMethod = element.Method;
145+
const currentStartLine = Number(element.StartLine);
113146
if (previousMethod && previousStartLine) {
114-
const start = new vscode.Position(Number(previousStartLine) - 1, 0);
115-
const end = new vscode.Position(Number(element.StartLine) - 2, Number.MAX_VALUE);
147+
const start = new vscode.Position(previousStartLine - 1 + startOffset, 0);
148+
const end = new vscode.Position(currentStartLine - 2 + endOffset, Number.MAX_VALUE);
116149
detailedCoverage.push(new vscode.DeclarationCoverage(previousMethod, true, new vscode.Range(start, end)));
117150
}
118-
previousMethod = element.Method;
119-
previousStartLine = Number(element.StartLine);
151+
startOffset = endOffset;
152+
endOffset = (mapOffsets.get(currentMethod) || endOffset);
153+
previousMethod = currentMethod;
154+
previousStartLine = currentStartLine;
120155
});
156+
157+
// Add the final method (if any)
121158
if (previousMethod && previousStartLine) {
122-
const start = new vscode.Position(Number(previousStartLine) - 1, 0);
123-
const end = new vscode.Position(Number.MAX_VALUE, Number.MAX_VALUE);
159+
const start = new vscode.Position(previousStartLine - 1 + startOffset, 0);
160+
const end = new vscode.Position(Number.MAX_VALUE, Number.MAX_VALUE); // Hack that will cover the rest of the file, not just the the final method
124161
detailedCoverage.push(new vscode.DeclarationCoverage(previousMethod, true, new vscode.Range(start, end)));
125162
}
126163
}

0 commit comments

Comments
 (0)