Skip to content

Commit ced1ec6

Browse files
committed
refactor supplemental context generation into diffGenerator
1 parent 808d95e commit ced1ec6

File tree

3 files changed

+143
-129
lines changed

3 files changed

+143
-129
lines changed

packages/core/src/codewhisperer/nextEditPrediction/PredictionTracker.ts

Lines changed: 23 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@ import * as vscode from 'vscode'
77
import * as path from 'path'
88
import fs from '../../shared/fs/fs'
99
import { getLogger } from '../../shared/logger/logger'
10-
import { DiffGenerator } from './diffGenerator'
10+
import * as diffGenerator from './diffGenerator'
1111
import * as codewhispererClient from '../client/codewhisperer'
1212

1313
export interface FileTrackerConfig {
1414
/** Maximum number of files to track (default: 15) */
1515
maxFiles: number
1616
/** Maximum total size in kilobytes (default: 200) */
1717
maxTotalSizeKb: number
18-
/** Maximum size per file in kilobytes */
19-
maxFileSizeKb: number
2018
/** Debounce interval in milliseconds (default: 2000) */
2119
debounceIntervalMs: number
2220
/** Maximum age of snapshots in milliseconds (default: 30000) */
@@ -49,7 +47,6 @@ export class PredictionTracker {
4947
const defaultConfig = {
5048
maxFiles: 25,
5149
maxTotalSizeKb: 50000,
52-
maxFileSizeKb: 100, // Default max size per file
5350
debounceIntervalMs: 2000,
5451
maxAgeMs: 30000, // 30 sec
5552
maxSupplementalContext: 15, // Default max supplemental contexts
@@ -90,12 +87,6 @@ export class PredictionTracker {
9087
const content = previousContent
9188
const size = Buffer.byteLength(content, 'utf8')
9289

93-
// Skip if the file is too large
94-
if (size > this.config.maxFileSizeKb * 1024) {
95-
getLogger().info(`File ${filePath} exceeds maximum size limit`)
96-
return
97-
}
98-
9990
const timestamp = Date.now()
10091
const storageKey = this.generateStorageKey(filePath, timestamp)
10192

@@ -291,7 +282,7 @@ export class PredictionTracker {
291282
throw new Error('Storage path not available')
292283
}
293284

294-
const snapshotsDir = path.join(this.storagePath, 'file-snapshots')
285+
const snapshotsDir = path.join(this.storagePath, 'AmazonQ-file-snapshots')
295286
if (!(await fs.existsDir(snapshotsDir))) {
296287
await fs.mkdir(snapshotsDir)
297288
}
@@ -309,7 +300,7 @@ export class PredictionTracker {
309300
return
310301
}
311302

312-
const snapshotsDir = path.join(this.storagePath, 'file-snapshots')
303+
const snapshotsDir = path.join(this.storagePath, 'AmazonQ-file-snapshots')
313304
const filePath = path.join(snapshotsDir, `${snapshot.storageKey}.content`)
314305

315306
if (await fs.exists(filePath)) {
@@ -331,7 +322,7 @@ export class PredictionTracker {
331322
throw new Error('Storage path not available')
332323
}
333324

334-
const snapshotsDir = path.join(this.storagePath, 'file-snapshots')
325+
const snapshotsDir = path.join(this.storagePath, 'AmazonQ-file-snapshots')
335326
const filePath = path.join(snapshotsDir, `${snapshot.storageKey}.content`)
336327

337328
try {
@@ -346,8 +337,6 @@ export class PredictionTracker {
346337
* Generates unified diffs between adjacent snapshots of a file
347338
* and between the newest snapshot and the current file content
348339
*
349-
* @param filePath Path to the file for which diffs should be generated
350-
* @param currentContent Current content of the file to compare with the latest snapshot
351340
* @returns Array of SupplementalContext objects containing diffs between snapshots and current content
352341
*/
353342
public async generatePredictionSupplementalContext(): Promise<codewhispererClient.SupplementalContext[]> {
@@ -357,97 +346,44 @@ export class PredictionTracker {
357346
}
358347
const filePath = activeEditor.document.uri.fsPath
359348
const currentContent = activeEditor.document.getText()
349+
360350
// Get all snapshots for this file
361351
const snapshots = this.getFileSnapshots(filePath)
362352

363353
if (snapshots.length === 0) {
364354
return []
365355
}
366356

367-
// Sort snapshots by timestamp (oldest first)
368-
const sortedSnapshots = [...snapshots].sort((a, b) => a.timestamp - b.timestamp)
369-
const supplementalContexts: codewhispererClient.SupplementalContext[] = []
370-
const currentTimestamp = Date.now()
371-
372-
// Generate diffs between adjacent snapshots
373-
for (let i = 0; i < sortedSnapshots.length - 1; i++) {
374-
const oldSnapshot = sortedSnapshots[i]
375-
const newSnapshot = sortedSnapshots[i + 1]
376-
377-
try {
378-
const oldContent = await this.getSnapshotContent(oldSnapshot)
379-
const newContent = await this.getSnapshotContent(newSnapshot)
380-
381-
const diff = await DiffGenerator.generateUnifiedDiffWithTimestamps(
382-
oldSnapshot.filePath,
383-
newSnapshot.filePath,
384-
oldContent,
385-
newContent,
386-
oldSnapshot.timestamp,
387-
newSnapshot.timestamp
388-
)
389-
390-
supplementalContexts.push({
391-
filePath: oldSnapshot.filePath,
392-
content: diff,
393-
type: 'PreviousEditorState',
394-
metadata: {
395-
previousEditorStateMetadata: {
396-
timeOffset: currentTimestamp - oldSnapshot.timestamp,
397-
},
398-
},
399-
})
400-
} catch (err) {
401-
getLogger().error(`Failed to generate diff: ${err}`)
402-
}
403-
}
404-
405-
// Generate diff between the newest snapshot and the current file content
406-
if (sortedSnapshots.length > 0) {
407-
const newestSnapshot = sortedSnapshots[sortedSnapshots.length - 1]
408-
357+
// Load all snapshot contents
358+
const snapshotContents: diffGenerator.SnapshotContent[] = []
359+
for (const snapshot of snapshots) {
409360
try {
410-
// Need to temporarily save files to compare
411-
const newestContent = await this.getSnapshotContent(newestSnapshot)
412-
413-
const diff = await DiffGenerator.generateUnifiedDiffWithTimestamps(
414-
newestSnapshot.filePath,
415-
newestSnapshot.filePath,
416-
newestContent,
417-
currentContent,
418-
newestSnapshot.timestamp,
419-
currentTimestamp
420-
)
421-
422-
supplementalContexts.push({
423-
filePath: newestSnapshot.filePath,
424-
content: diff,
425-
type: 'PreviousEditorState',
426-
metadata: {
427-
previousEditorStateMetadata: {
428-
timeOffset: currentTimestamp - newestSnapshot.timestamp,
429-
},
430-
},
361+
const content = await this.getSnapshotContent(snapshot)
362+
snapshotContents.push({
363+
filePath: snapshot.filePath,
364+
content,
365+
timestamp: snapshot.timestamp,
431366
})
432367
} catch (err) {
433-
getLogger().error(`Failed to generate diff with current content: ${err}`)
368+
getLogger().error(`Failed to load snapshot content: ${err}`)
434369
}
435370
}
436371

437-
// Limit the number of supplemental contexts based on config
438-
if (supplementalContexts.length > this.config.maxSupplementalContext) {
439-
return supplementalContexts.slice(-this.config.maxSupplementalContext)
440-
}
441-
442-
return supplementalContexts
372+
// Use the diffGenerator module to generate supplemental contexts
373+
return diffGenerator.generateDiffContexts(
374+
filePath,
375+
currentContent,
376+
snapshotContents,
377+
this.config.maxSupplementalContext
378+
)
443379
}
444380

445381
private async ensureStorageDirectoryExists(): Promise<void> {
446382
if (!this.storagePath) {
447383
return
448384
}
449385

450-
const snapshotsDir = path.join(this.storagePath, 'file-snapshots')
386+
const snapshotsDir = path.join(this.storagePath, 'AmazonQ-file-snapshots')
451387
if (!(await fs.existsDir(snapshotsDir))) {
452388
await fs.mkdir(snapshotsDir)
453389
}
@@ -458,7 +394,7 @@ export class PredictionTracker {
458394
return
459395
}
460396

461-
const snapshotsDir = path.join(this.storagePath, 'file-snapshots')
397+
const snapshotsDir = path.join(this.storagePath, 'AmazonQ-file-snapshots')
462398
if (!(await fs.existsDir(snapshotsDir))) {
463399
return
464400
}

packages/core/src/codewhisperer/nextEditPrediction/SnapshotVisualizer.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class SnapshotVisualizer {
5454
case 'showSnapshot':
5555
await this.showSnapshotContent(message.filePath, message.timestamp)
5656
break
57-
case 'fireAPI':
57+
case 'generateUdiff':
5858
await this.generateDiffs()
5959
break
6060
}
@@ -266,7 +266,7 @@ export class SnapshotVisualizer {
266266
<div class="button-group">
267267
<span id="total-count">0 snapshots</span>
268268
<button class="btn" id="refresh-btn">Refresh</button>
269-
<button class="btn" id="fire-api-btn">FireAPI</button>
269+
<button class="btn" id="generate-udiff-btn">generateUdiff</button>
270270
</div>
271271
</div>
272272
@@ -404,9 +404,9 @@ export class SnapshotVisualizer {
404404
vscode.postMessage({ command: 'refresh' });
405405
});
406406
407-
// FireAPI button handler
408-
document.getElementById('fire-api-btn').addEventListener('click', () => {
409-
vscode.postMessage({ command: 'fireAPI' });
407+
// generateUdiff button handler
408+
document.getElementById('generate-udiff-btn').addEventListener('click', () => {
409+
vscode.postMessage({ command: 'generateUdiff' });
410410
});
411411
412412
// Request initial data

packages/core/src/codewhisperer/nextEditPrediction/diffGenerator.ts

Lines changed: 115 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,125 @@
44
*/
55

66
import * as diff from 'diff'
7+
import { getLogger } from '../../shared/logger/logger'
8+
import * as codewhispererClient from '../client/codewhisperer'
79

810
/**
9-
* Class to generate a unified diff format between old and new file contents
11+
* Generates a unified diff format between old and new file contents
12+
*
13+
* @param oldFilePath - Path of the old file
14+
* @param newFilePath - Path of the new file
15+
* @param oldContent - Content of the old file
16+
* @param newContent - Content of the new file
17+
* @param oldTimestamp - Timestamp of the old file version
18+
* @param newTimestamp - Timestamp of the new file version
19+
* @param contextSize - Number of context lines to include (default: 3)
20+
* @returns Unified diff as a string
1021
*/
11-
export class DiffGenerator {
12-
/**
13-
* @param oldFilePath - Path of the old file
14-
* @param newFilePath - Path of the new file
15-
* @param oldContent - Content of the old file
16-
* @param newContent - Content of the new file
17-
* @param oldTimestamp - Timestamp of the old file version
18-
* @param newTimestamp - Timestamp of the new file version
19-
* @param contextSize - Number of context lines to include (default: 3)
20-
* @returns Unified diff as a string
21-
*/
22-
public static async generateUnifiedDiffWithTimestamps(
23-
oldFilePath: string,
24-
newFilePath: string,
25-
oldContent: string,
26-
newContent: string,
27-
oldTimestamp: number,
28-
newTimestamp: number,
29-
contextSize: number = 3
30-
): Promise<string> {
31-
const patchResult = diff.createTwoFilesPatch(
32-
oldFilePath,
33-
newFilePath,
34-
oldContent,
35-
newContent,
36-
`${oldTimestamp}`, // Old file label with timestamp
37-
`${newTimestamp}`, // New file label with timestamp
38-
{ context: contextSize }
39-
)
40-
41-
// Remove the "Index:" line and the separator line that follows it
42-
const lines = patchResult.split('\n')
43-
if (lines.length >= 2 && lines[0].startsWith('Index:')) {
44-
lines.splice(0, 2)
45-
return lines.join('\n')
22+
export async function generateUnifiedDiffWithTimestamps(
23+
oldFilePath: string,
24+
newFilePath: string,
25+
oldContent: string,
26+
newContent: string,
27+
oldTimestamp: number,
28+
newTimestamp: number,
29+
contextSize: number = 3
30+
): Promise<string> {
31+
const patchResult = diff.createTwoFilesPatch(
32+
oldFilePath,
33+
newFilePath,
34+
oldContent,
35+
newContent,
36+
`${oldTimestamp}`, // Old file label with timestamp
37+
`${newTimestamp}`, // New file label with timestamp
38+
{ context: contextSize }
39+
)
40+
41+
// Remove unused headers
42+
const lines = patchResult.split('\n')
43+
if (lines.length >= 2 && lines[0].startsWith('Index:')) {
44+
lines.splice(0, 2)
45+
return lines.join('\n')
46+
}
47+
48+
return patchResult
49+
}
50+
51+
/**
52+
* Interface for snapshot content with timestamp
53+
*/
54+
export interface SnapshotContent {
55+
filePath: string
56+
content: string
57+
timestamp: number
58+
}
59+
60+
/**
61+
* Generates supplemental contexts from snapshot contents and current content
62+
*
63+
* @param filePath - Path to the file
64+
* @param currentContent - Current content of the file
65+
* @param snapshotContents - List of snapshot contents sorted by timestamp (oldest first)
66+
* @param maxContexts - Maximum number of supplemental contexts to return
67+
* @returns Array of SupplementalContext objects
68+
*/
69+
export async function generateDiffContexts(
70+
filePath: string,
71+
currentContent: string,
72+
snapshotContents: SnapshotContent[],
73+
maxContexts: number
74+
): Promise<codewhispererClient.SupplementalContext[]> {
75+
if (snapshotContents.length === 0) {
76+
return []
77+
}
78+
79+
const supplementalContexts: codewhispererClient.SupplementalContext[] = []
80+
const currentTimestamp = Date.now()
81+
82+
// Treat current content as the last snapshot
83+
const allContents = [
84+
...snapshotContents,
85+
{
86+
filePath,
87+
content: currentContent,
88+
timestamp: currentTimestamp,
89+
},
90+
]
91+
92+
// Generate diffs between all adjacent snapshots (including current content)
93+
for (let i = 0; i < allContents.length - 1; i++) {
94+
const oldSnapshot = allContents[i]
95+
const newSnapshot = allContents[i + 1]
96+
97+
try {
98+
const diff = await generateUnifiedDiffWithTimestamps(
99+
oldSnapshot.filePath,
100+
newSnapshot.filePath,
101+
oldSnapshot.content,
102+
newSnapshot.content,
103+
oldSnapshot.timestamp,
104+
newSnapshot.timestamp
105+
)
106+
107+
supplementalContexts.push({
108+
filePath: oldSnapshot.filePath,
109+
content: diff,
110+
type: 'PreviousEditorState',
111+
metadata: {
112+
previousEditorStateMetadata: {
113+
timeOffset: currentTimestamp - oldSnapshot.timestamp,
114+
},
115+
},
116+
})
117+
} catch (err) {
118+
getLogger().error(`Failed to generate diff: ${err}`)
46119
}
120+
}
47121

48-
return patchResult
122+
// Limit the number of supplemental contexts based on config
123+
if (supplementalContexts.length > maxContexts) {
124+
return supplementalContexts.slice(-maxContexts)
49125
}
126+
127+
return supplementalContexts
50128
}

0 commit comments

Comments
 (0)