33 * SPDX-License-Identifier: Apache-2.0
44 */
55
6- import { featureName , Mode } from '../constants'
6+ import { docScheme , featureName , Mode } from '../constants'
77import { DeletedFileInfo , Interaction , NewFileInfo , SessionState , SessionStateConfig } from '../types'
88import { PrepareCodeGenState } from './sessionState'
99import { telemetry } from '../../shared/telemetry/telemetry'
@@ -19,13 +19,14 @@ import { logWithConversationId } from '../../amazonqFeatureDev/userFacingText'
1919import { ConversationIdNotFoundError } from '../../amazonqFeatureDev/errors'
2020import { referenceLogText } from '../../amazonqFeatureDev/constants'
2121import {
22- DocGenerationEvent ,
23- DocGenerationInteractionType ,
22+ DocInteractionType ,
23+ DocV2AcceptanceEvent ,
24+ DocV2GenerationEvent ,
2425 SendTelemetryEventRequest ,
2526} from '../../codewhisperer/client/codewhispereruserclient'
26- import { getDiffCharsAndLines } from '../../shared/utilities/diffUtils'
2727import { getClientId , getOperatingSystem , getOptOutPreference } from '../../shared/telemetry/util'
2828import { DocMessenger } from '../messenger'
29+ import { computeDiff } from '../../amazonq/commons/diff'
2930
3031export class Session {
3132 private _state ?: SessionState | Omit < SessionState , 'uploadId' >
@@ -38,6 +39,7 @@ export class Session {
3839
3940 // Used to keep track of whether or not the current session is currently authenticating/needs authenticating
4041 public isAuthenticating : boolean
42+ private _reportedDocChanges : string | undefined = undefined
4143
4244 constructor (
4345 public readonly config : SessionConfig ,
@@ -177,41 +179,109 @@ export class Session {
177179 }
178180 }
179181
180- public async countAddedContent ( interactionType ?: DocGenerationInteractionType ) {
182+ public async countGeneratedContent ( interactionType ?: DocInteractionType ) {
183+ let totalGeneratedChars = 0
184+ let totalGeneratedLines = 0
185+ let totalGeneratedFiles = 0
186+ const filePaths = this . state . filePaths ?? [ ]
187+
188+ for ( const filePath of filePaths ) {
189+ if ( interactionType === 'GENERATE_README' ) {
190+ if ( this . _reportedDocChanges ) {
191+ const { charsAdded, linesAdded } = await this . computeFilePathDiff (
192+ filePath ,
193+ this . _reportedDocChanges
194+ )
195+ totalGeneratedChars += charsAdded
196+ totalGeneratedLines += linesAdded
197+ } else {
198+ const fileContent = filePath . fileContent
199+ totalGeneratedChars += fileContent . length
200+ totalGeneratedLines += fileContent . split ( '\n' ) . length
201+ }
202+ } else {
203+ const { charsAdded, linesAdded } = await this . computeFilePathDiff ( filePath , this . _reportedDocChanges )
204+ totalGeneratedChars += charsAdded
205+ totalGeneratedLines += linesAdded
206+ }
207+ this . _reportedDocChanges = filePath . fileContent
208+ totalGeneratedFiles += 1
209+ }
210+ return {
211+ totalGeneratedChars,
212+ totalGeneratedLines,
213+ totalGeneratedFiles,
214+ }
215+ }
216+
217+ public async countAddedContent ( interactionType ?: DocInteractionType ) {
181218 let totalAddedChars = 0
182219 let totalAddedLines = 0
183220 let totalAddedFiles = 0
184-
185- for ( const filePath of this . state . filePaths ?. filter ( ( i ) => ! i . rejected ) ?? [ ] ) {
186- const absolutePath = path . join ( filePath . workspaceFolder . uri . fsPath , filePath . relativePath )
187- const uri = filePath . virtualMemoryUri
188- const content = await this . config . fs . readFile ( uri )
189- const decodedContent = new TextDecoder ( ) . decode ( content )
190- totalAddedFiles += 1
191-
192- if ( ( await fs . exists ( absolutePath ) ) && interactionType === 'UPDATE_README' ) {
193- const existingContent = await fs . readFileText ( absolutePath )
194- const { addedChars, addedLines } = getDiffCharsAndLines ( existingContent , decodedContent )
195- totalAddedChars += addedChars
196- totalAddedLines += addedLines
221+ const newFilePaths =
222+ this . state . filePaths ?. filter ( ( filePath ) => ! filePath . rejected && ! filePath . changeApplied ) ?? [ ]
223+
224+ for ( const filePath of newFilePaths ) {
225+ if ( interactionType === 'GENERATE_README' ) {
226+ const fileContent = filePath . fileContent
227+ totalAddedChars += fileContent . length
228+ totalAddedLines += fileContent . split ( '\n' ) . length
197229 } else {
198- totalAddedChars += decodedContent . length
199- totalAddedLines += decodedContent . split ( '\n' ) . length
230+ const { charsAdded, linesAdded } = await this . computeFilePathDiff ( filePath )
231+ totalAddedChars += charsAdded
232+ totalAddedLines += linesAdded
200233 }
234+ totalAddedFiles += 1
201235 }
202-
203236 return {
204237 totalAddedChars,
205238 totalAddedLines,
206239 totalAddedFiles,
207240 }
208241 }
209- public async sendDocGenerationTelemetryEvent ( docGenerationEvent : DocGenerationEvent ) {
242+
243+ public async computeFilePathDiff ( filePath : NewFileInfo , reportedChanges ?: string ) {
244+ const leftPath = `${ filePath . workspaceFolder . uri . fsPath } /${ filePath . relativePath } `
245+ const rightPath = filePath . virtualMemoryUri . path
246+ const diff = await computeDiff ( leftPath , rightPath , this . tabID , docScheme , reportedChanges )
247+ return { leftPath, rightPath, ...diff }
248+ }
249+
250+ public async sendDocGenerationTelemetryEvent ( docV2GenerationEvent : DocV2GenerationEvent ) {
251+ const client = await this . proxyClient . getClient ( )
252+ try {
253+ const params : SendTelemetryEventRequest = {
254+ telemetryEvent : {
255+ docV2GenerationEvent,
256+ } ,
257+ optOutPreference : getOptOutPreference ( ) ,
258+ userContext : {
259+ ideCategory : 'VSCODE' ,
260+ operatingSystem : getOperatingSystem ( ) ,
261+ product : 'DocGeneration' , // Should be the same as in JetBrains
262+ clientId : getClientId ( globals . globalState ) ,
263+ ideVersion : extensionVersion ,
264+ } ,
265+ }
266+ const response = await client . sendTelemetryEvent ( params ) . promise ( )
267+ getLogger ( ) . debug (
268+ `${ featureName } : successfully sent docV2GenerationEvent: ConversationId: ${ docV2GenerationEvent . conversationId } RequestId: ${ response . $response . requestId } `
269+ )
270+ } catch ( e ) {
271+ getLogger ( ) . error (
272+ `${ featureName } : failed to send doc generation telemetry: ${ ( e as Error ) . name } : ${
273+ ( e as Error ) . message
274+ } RequestId: ${ ( e as any ) . requestId } `
275+ )
276+ }
277+ }
278+
279+ public async sendDocAcceptanceTelemetryEvent ( docV2AcceptanceEvent : DocV2AcceptanceEvent ) {
210280 const client = await this . proxyClient . getClient ( )
211281 try {
212282 const params : SendTelemetryEventRequest = {
213283 telemetryEvent : {
214- docGenerationEvent ,
284+ docV2AcceptanceEvent ,
215285 } ,
216286 optOutPreference : getOptOutPreference ( ) ,
217287 userContext : {
@@ -224,7 +294,7 @@ export class Session {
224294 }
225295 const response = await client . sendTelemetryEvent ( params ) . promise ( )
226296 getLogger ( ) . debug (
227- `${ featureName } : successfully sent docGenerationEvent : ConversationId: ${ docGenerationEvent . conversationId } RequestId: ${ response . $response . requestId } `
297+ `${ featureName } : successfully sent docV2AcceptanceEvent : ConversationId: ${ docV2AcceptanceEvent . conversationId } RequestId: ${ response . $response . requestId } `
228298 )
229299 } catch ( e ) {
230300 getLogger ( ) . error (
0 commit comments