Skip to content

Commit 8e04901

Browse files
authored
fix(codewhisperer): Improve Code percentage reporting (#4350)
2 parents 84a12a8 + b5dc645 commit 8e04901

File tree

6 files changed

+67
-28
lines changed

6 files changed

+67
-28
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "CodeWhisperer: Improve CodePercentage telemetry reporting"
4+
}

src/codewhisperer/client/user-service-2.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@
417417
"programmingLanguage": { "shape": "ProgrammingLanguage" },
418418
"acceptedCharacterCount": { "shape": "PrimitiveInteger" },
419419
"totalCharacterCount": { "shape": "PrimitiveInteger" },
420-
"timestamp": { "shape": "Timestamp" }
420+
"timestamp": { "shape": "Timestamp" },
421+
"unmodifiedAcceptedCharacterCount": { "shape": "PrimitiveInteger" }
421422
}
422423
},
423424
"CodeGenerationId": {

src/codewhisperer/commands/onAcceptance.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ export async function onAcceptance(acceptanceEntry: OnRecommendationAcceptanceEn
6666
completionType: acceptanceEntry.completionType,
6767
language: languageContext.language,
6868
})
69-
const codeRangeAfterFormat = new vscode.Range(start, acceptanceEntry.editor.selection.active)
69+
const insertedCoderange = new vscode.Range(start, end)
7070
CodeWhispererCodeCoverageTracker.getTracker(languageContext.language)?.countAcceptedTokens(
71-
codeRangeAfterFormat,
72-
acceptanceEntry.editor.document.getText(codeRangeAfterFormat),
71+
insertedCoderange,
72+
acceptanceEntry.editor.document.getText(insertedCoderange),
7373
acceptanceEntry.editor.document.fileName
7474
)
7575
if (acceptanceEntry.references !== undefined) {

src/codewhisperer/commands/onInlineAcceptance.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ export async function onInlineAcceptance(
116116
completionType: acceptanceEntry.completionType,
117117
language: languageContext.language,
118118
})
119-
const codeRangeAfterFormat = new vscode.Range(start, acceptanceEntry.editor.selection.active)
119+
const insertedCoderange = new vscode.Range(start, end)
120120
CodeWhispererCodeCoverageTracker.getTracker(languageContext.language, globalStorage)?.countAcceptedTokens(
121-
codeRangeAfterFormat,
122-
acceptanceEntry.editor.document.getText(codeRangeAfterFormat),
121+
insertedCoderange,
122+
acceptanceEntry.editor.document.getText(insertedCoderange),
123123
acceptanceEntry.editor.document.fileName
124124
)
125125
if (acceptanceEntry.references !== undefined) {

src/codewhisperer/tracker/codewhispererCodeCoverageTracker.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,6 @@ export class CodeWhispererCodeCoverageTracker {
5959
return TelemetryHelper.instance.isTelemetryEnabled() && AuthUtil.instance.isConnected()
6060
}
6161

62-
public countAcceptedTokens(range: vscode.Range, text: string, filename: string) {
63-
if (!this.isActive()) {
64-
return
65-
}
66-
// generate accepted recommendation token and stored in collection
67-
this.addAcceptedTokens(filename, { range: range, text: text, accepted: text.length })
68-
}
69-
7062
public incrementServiceInvocationCount() {
7163
this._serviceInvocationCount += 1
7264
}
@@ -85,6 +77,8 @@ export class CodeWhispererCodeCoverageTracker {
8577
}
8678
}
8779

80+
// TODO: Improve the range tracking of the accepted recommendation
81+
// TODO: use the editor of the filename, not the current editor
8882
public updateAcceptedTokensCount(editor: vscode.TextEditor) {
8983
const filename = editor.document.fileName
9084
if (filename in this._acceptedTokens) {
@@ -112,17 +106,25 @@ export class CodeWhispererCodeCoverageTracker {
112106
if (vscode.window.activeTextEditor) {
113107
this.updateAcceptedTokensCount(vscode.window.activeTextEditor)
114108
}
109+
// the accepted characters without counting user modification
115110
let acceptedTokens = 0
111+
// the accepted characters after calculating user modificaiton
112+
let unmodifiedAcceptedTokens = 0
116113
for (const filename in this._acceptedTokens) {
117114
this._acceptedTokens[filename].forEach(v => {
118115
if (filename in this._totalTokens && this._totalTokens[filename] >= v.accepted) {
119-
acceptedTokens += v.accepted
116+
unmodifiedAcceptedTokens += v.accepted
117+
acceptedTokens += v.text.length
120118
}
121119
})
122120
}
123121
const percentCount = ((acceptedTokens / totalTokens) * 100).toFixed(2)
124122
const percentage = Math.round(parseInt(percentCount))
125123
const selectedCustomization = getSelectedCustomization()
124+
if (this._serviceInvocationCount <= 0) {
125+
getLogger().debug(`Skip emiting code contribution metric`)
126+
return
127+
}
126128
telemetry.codewhisperer_codePercentage.emit({
127129
codewhispererTotalTokens: totalTokens,
128130
codewhispererLanguage: this._language,
@@ -142,6 +144,7 @@ export class CodeWhispererCodeCoverageTracker {
142144
languageName: runtimeLanguageContext.toRuntimeLanguage(this._language),
143145
},
144146
acceptedCharacterCount: acceptedTokens,
147+
unmodifiedAcceptedCharacterCount: unmodifiedAcceptedTokens,
145148
totalCharacterCount: totalTokens,
146149
timestamp: new Date(Date.now()),
147150
},
@@ -226,15 +229,24 @@ export class CodeWhispererCodeCoverageTracker {
226229
}
227230
}
228231

232+
public countAcceptedTokens(range: vscode.Range, text: string, filename: string) {
233+
if (!this.isActive()) {
234+
return
235+
}
236+
// generate accepted recommendation token and stored in collection
237+
this.addAcceptedTokens(filename, { range: range, text: text, accepted: text.length })
238+
this.addTotalTokens(filename, text.length)
239+
}
240+
229241
public countTotalTokens(e: vscode.TextDocumentChangeEvent) {
230242
// ignore no contentChanges. ignore contentChanges from other plugins (formatters)
231-
// only include contentChanges from user action.
243+
// only include contentChanges from user keystroke input(one character input).
232244
// Also ignore deletion events due to a known issue of tracking deleted CodeWhiperer tokens.
233245
if (
234246
!runtimeLanguageContext.isLanguageSupported(e.document.languageId) ||
235247
vsCodeState.isCodeWhispererEditing ||
236248
e.contentChanges.length !== 1 ||
237-
e.contentChanges[0].text.length === 0
249+
e.contentChanges[0].text.length !== 1
238250
) {
239251
return
240252
}

src/test/codewhisperer/tracker/codewhispererCodeCoverageTracker.test.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,14 @@ describe('codewhispererCodecoverageTracker', function () {
206206
accepted: 1,
207207
})
208208
})
209+
it('Should increase TotalTokens', function () {
210+
if (!tracker) {
211+
assert.fail()
212+
}
213+
tracker.countAcceptedTokens(new vscode.Range(0, 0, 0, 1), 'a', 'test.py')
214+
tracker.countAcceptedTokens(new vscode.Range(0, 0, 0, 1), 'b', 'test.py')
215+
assert.deepStrictEqual(tracker.totalTokens['test.py'], 2)
216+
})
209217
})
210218

211219
describe('countTotalTokens', function () {
@@ -224,7 +232,7 @@ describe('codewhispererCodecoverageTracker', function () {
224232
CodeWhispererCodeCoverageTracker.instances.clear()
225233
})
226234

227-
it('Should skip when user copy large files', function () {
235+
it('Should skip when content change size is not 1', function () {
228236
if (!tracker) {
229237
assert.fail()
230238
}
@@ -266,7 +274,7 @@ describe('codewhispererCodecoverageTracker', function () {
266274
assert.ok(!startedSpy.called)
267275
})
268276

269-
it('Should reduce tokens when delete', function () {
277+
it('Should not reduce tokens when delete', function () {
270278
if (!tracker) {
271279
assert.fail()
272280
}
@@ -276,14 +284,26 @@ describe('codewhispererCodecoverageTracker', function () {
276284
document: doc,
277285
contentChanges: [
278286
{
279-
range: new vscode.Range(0, 0, 0, 3),
287+
range: new vscode.Range(0, 0, 0, 1),
280288
rangeOffset: 0,
281289
rangeLength: 0,
282-
text: 'aaa',
290+
text: 'a',
283291
},
284292
],
285293
})
286-
assert.strictEqual(tracker?.totalTokens[doc.fileName], 3)
294+
tracker.countTotalTokens({
295+
reason: undefined,
296+
document: doc,
297+
contentChanges: [
298+
{
299+
range: new vscode.Range(0, 0, 0, 1),
300+
rangeOffset: 0,
301+
rangeLength: 0,
302+
text: 'b',
303+
},
304+
],
305+
})
306+
assert.strictEqual(tracker?.totalTokens[doc.fileName], 2)
287307
tracker.countTotalTokens({
288308
reason: undefined,
289309
document: doc,
@@ -296,7 +316,7 @@ describe('codewhispererCodecoverageTracker', function () {
296316
},
297317
],
298318
})
299-
assert.strictEqual(tracker?.totalTokens[doc.fileName], 3)
319+
assert.strictEqual(tracker?.totalTokens[doc.fileName], 2)
300320
})
301321

302322
it('Should add tokens when type', function () {
@@ -381,21 +401,21 @@ describe('codewhispererCodecoverageTracker', function () {
381401
const tracker = CodeWhispererCodeCoverageTracker.getTracker(language)
382402

383403
const assertTelemetry = assertTelemetryCurried('codewhisperer_codePercentage')
404+
tracker?.incrementServiceInvocationCount()
384405
tracker?.addAcceptedTokens(`test.py`, { range: new vscode.Range(0, 0, 0, 7), text: `print()`, accepted: 7 })
385406
tracker?.addTotalTokens(`test.py`, 100)
386407
tracker?.emitCodeWhispererCodeContribution()
387-
388408
assertTelemetry({
389409
codewhispererTotalTokens: 100,
390410
codewhispererLanguage: language,
391411
codewhispererAcceptedTokens: 7,
392412
codewhispererPercentage: 7,
393-
successCount: 0,
413+
successCount: 1,
394414
codewhispererUserGroup: 'Control',
395415
})
396416
})
397417

398-
it('should emit correct code coverage telemetry in java file', async function () {
418+
it('should emit correct code coverage telemetry when success count = 0', async function () {
399419
await globals.context.globalState.update(CodeWhispererConstants.userGroupKey, {
400420
group: CodeWhispererConstants.UserGroup.Control,
401421
version: extensionVersion,
@@ -409,14 +429,16 @@ describe('codewhispererCodecoverageTracker', function () {
409429
text: `public static main`,
410430
accepted: 18,
411431
})
432+
tracker?.incrementServiceInvocationCount()
433+
tracker?.incrementServiceInvocationCount()
412434
tracker?.addTotalTokens(`test.java`, 30)
413435
tracker?.emitCodeWhispererCodeContribution()
414436
assertTelemetry({
415437
codewhispererTotalTokens: 30,
416438
codewhispererLanguage: 'java',
417439
codewhispererAcceptedTokens: 18,
418440
codewhispererPercentage: 60,
419-
successCount: 0,
441+
successCount: 2,
420442
codewhispererUserGroup: 'Control',
421443
})
422444
})

0 commit comments

Comments
 (0)