66import * as vscode from 'vscode'
77import { getLogger } from '../../shared/logger/logger'
88import * as CodeWhispererConstants from '../models/constants'
9- import { OnRecommendationAcceptanceEntry , vsCodeState } from '../models/model'
109import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
1110import { TelemetryHelper } from '../util/telemetryHelper'
1211import { AuthUtil } from '../util/authUtil'
@@ -15,22 +14,37 @@ import { codeWhispererClient as client } from '../client/codewhisperer'
1514import { isAwsError } from '../../shared/errors'
1615
1716/**
18- * This singleton class is mainly used for calculating the total code written by Amazon Q and user
19- * It is meant to replace `CodeWhispererCodeCoverageTracker`
17+ * This singleton class is mainly used for calculating the user written code
18+ * for active Amazon Q users.
19+ * It reports the user written code per 5 minutes when the user is coding and using Amazon Q features
2020 */
21- export class QCodeGenTracker {
22- private _totalNewCodeCharacterCount : number
23- private _totalNewCodeLineCount : number
21+ export class UserWrittenCodeTracker {
22+ private _userWrittenNewCodeCharacterCount : number
23+ private _userWrittenNewCodeLineCount : number
24+ private _qIsMakingEdits : boolean
2425 private _timer ?: NodeJS . Timer
2526 private _qUsageCount : number
27+ private _lastQInvocationTime : number
2628
27- static #instance: QCodeGenTracker
29+ static #instance: UserWrittenCodeTracker
2830 static copySnippetThreshold = 50
31+ static resetQIsEditingTimeoutMs = 5 * 60 * 1000
2932
3033 private constructor ( ) {
31- this . _totalNewCodeLineCount = 0
32- this . _totalNewCodeCharacterCount = 0
34+ this . _userWrittenNewCodeLineCount = 0
35+ this . _userWrittenNewCodeCharacterCount = 0
3336 this . _qUsageCount = 0
37+ this . _qIsMakingEdits = false
38+ this . _timer = undefined
39+ this . _lastQInvocationTime = 0
40+ }
41+
42+ private resetTracker ( ) {
43+ this . _userWrittenNewCodeLineCount = 0
44+ this . _userWrittenNewCodeCharacterCount = 0
45+ this . _qUsageCount = 0
46+ this . _qIsMakingEdits = false
47+ this . _lastQInvocationTime = 0
3448 }
3549
3650 public static get instance ( ) {
@@ -45,6 +59,15 @@ export class QCodeGenTracker {
4559 // for all Q features
4660 public onQFeatureInvoked ( ) {
4761 this . _qUsageCount += 1
62+ this . _lastQInvocationTime = performance . now ( )
63+ }
64+
65+ public onQStartsMakingEdits ( ) {
66+ this . _qIsMakingEdits = true
67+ }
68+
69+ public onQFinishesEdits ( ) {
70+ this . _qIsMakingEdits = false
4871 }
4972
5073 public emitCodeContribution ( ) {
@@ -60,8 +83,8 @@ export class QCodeGenTracker {
6083 acceptedCharacterCount : 0 ,
6184 totalCharacterCount : 0 ,
6285 timestamp : new Date ( Date . now ( ) ) ,
63- totalNewCodeCharacterCount : this . _totalNewCodeCharacterCount ,
64- totalNewCodeLineCount : this . _totalNewCodeLineCount ,
86+ userWrittenCodeCharacterCount : this . _userWrittenNewCodeCharacterCount ,
87+ userWrittenCodeLineCount : this . _userWrittenNewCodeLineCount ,
6588 } ,
6689 } ,
6790 } )
@@ -98,7 +121,7 @@ export class QCodeGenTracker {
98121 getLogger ( ) . debug ( `Skip emiting code contribution metric. There is no active Amazon Q usage. ` )
99122 return
100123 }
101- if ( this . _totalNewCodeCharacterCount === 0 ) {
124+ if ( this . _userWrittenNewCodeCharacterCount === 0 ) {
102125 getLogger ( ) . debug ( `Skip emiting code contribution metric. There is no new code added. ` )
103126 return
104127 }
@@ -113,12 +136,6 @@ export class QCodeGenTracker {
113136 } , CodeWhispererConstants . defaultCheckPeriodMillis )
114137 }
115138
116- private resetTracker ( ) {
117- this . _totalNewCodeLineCount = 0
118- this . _totalNewCodeCharacterCount = 0
119- this . _qUsageCount = 0
120- }
121-
122139 private closeTimer ( ) {
123140 if ( this . _timer !== undefined ) {
124141 clearTimeout ( this . _timer )
@@ -131,65 +148,32 @@ export class QCodeGenTracker {
131148 }
132149
133150 public onTextDocumentChange ( e : vscode . TextDocumentChangeEvent ) {
151+ // do not count code written by Q as user written code
134152 if (
135153 ! runtimeLanguageContext . isLanguageSupported ( e . document . languageId ) ||
136- vsCodeState . isCodeWhispererEditing ||
137- e . contentChanges . length === 0
154+ e . contentChanges . length === 0 ||
155+ this . _qIsMakingEdits
138156 ) {
157+ // if the boolean of qIsMakingEdits was incorrectly set to true
158+ // due to unhandled edge cases or early terminated code paths
159+ // reset it back to false after reasonabe period of time
160+ if ( this . _qIsMakingEdits ) {
161+ if ( performance . now ( ) - this . _lastQInvocationTime > UserWrittenCodeTracker . resetQIsEditingTimeoutMs ) {
162+ this . _qIsMakingEdits = false
163+ }
164+ }
139165 return
140166 }
141167 const contentChange = e . contentChanges [ 0 ]
142168 // if user copies code into the editor for more than 50 characters
143- // do not count this as total new code, this will skew the data.
144- if ( contentChange . text . length > QCodeGenTracker . copySnippetThreshold ) {
169+ // do not count this as total new code, this will skew the data,
170+ // reporting highly inflated user written code
171+ if ( contentChange . text . length > UserWrittenCodeTracker . copySnippetThreshold ) {
145172 return
146173 }
147- this . _totalNewCodeCharacterCount += contentChange . text . length
148- this . _totalNewCodeLineCount += this . countNewLines ( contentChange . text )
174+ this . _userWrittenNewCodeCharacterCount += contentChange . text . length
175+ this . _userWrittenNewCodeLineCount += this . countNewLines ( contentChange . text )
149176 // start 5 min data reporting once valid user input is detected
150177 this . tryStartTimer ( )
151178 }
152-
153- // add Q inline completion contributed code to total code written
154- public onInlineCompletionAcceptance ( acceptanceEntry : OnRecommendationAcceptanceEntry ) {
155- let typeaheadLength = 0
156- if ( acceptanceEntry . editor ) {
157- typeaheadLength = acceptanceEntry . editor . document . getText ( acceptanceEntry . range ) . length
158- }
159- const documentChangeLength = acceptanceEntry . recommendation . length - typeaheadLength
160- // if the inline completion is less than 50 characters, it will be auto captured by onTextDocumentChange
161- // notice that the document change event of such acceptance do not include typeahead
162- if ( documentChangeLength <= QCodeGenTracker . copySnippetThreshold ) {
163- return
164- }
165- this . _totalNewCodeCharacterCount += acceptanceEntry . recommendation . length
166- this . _totalNewCodeLineCount += this . countNewLines ( acceptanceEntry . recommendation )
167- }
168-
169- // add Q chat insert to cursor code to total code written
170- public onQChatInsertion ( acceptedCharacterCount ?: number , acceptedLineCount ?: number ) {
171- if ( acceptedCharacterCount && acceptedLineCount ) {
172- // if the chat inserted code is less than 50 characters, it will be auto captured by onTextDocumentChange
173- if ( acceptedCharacterCount <= QCodeGenTracker . copySnippetThreshold ) {
174- return
175- }
176- this . _totalNewCodeCharacterCount += acceptedCharacterCount
177- this . _totalNewCodeLineCount += acceptedLineCount
178- }
179- }
180-
181- // add Q inline chat acceptance to total code written
182- public onInlineChatAcceptance ( ) { }
183-
184- // TODO: add Q inline chat acceptance to total code written
185- public onTransformAcceptance ( ) { }
186-
187- // TODO: add Q feature dev acceptance to total code written
188- public onFeatureDevAcceptance ( ) { }
189-
190- // TODO: add Q UTG acceptance to total code written
191- public onUtgAcceptance ( ) { }
192-
193- // TODO: add Q UTG acceptance to total code written
194- public onDocAcceptance ( ) { }
195179}
0 commit comments