1
1
import * as vscode from 'vscode' ;
2
2
import * as fs from 'fs' ;
3
3
import * as path from 'path' ;
4
+
4
5
import { encodingForModel } from 'js-tiktoken' ;
5
6
import jschardet from 'jschardet' ;
6
7
import iconv from 'iconv-lite' ;
7
8
import throttle from 'lodash.throttle' ;
8
-
9
+ import { LRUCache } from 'lru-cache' ;
9
10
import { getLabelText , getLanguageFromFilename , stripLicenseHeaders , minifyCode } from './utils' ;
10
11
import { FileItem } from './file-item' ;
11
12
13
+ const options = {
14
+ max : 5_000 , // Maximum number of entries in the cache
15
+ } ;
16
+ const statCache = new LRUCache < string , fs . Stats > ( options ) ;
17
+ const fileProcessCache = new LRUCache < string , { mtime : number ; tokenCount : number } > ( options ) ;
18
+
19
+ async function getStat ( filePath : string ) : Promise < fs . Stats > {
20
+ if ( statCache . has ( filePath ) ) {
21
+ return statCache . get ( filePath ) ! ;
22
+ }
23
+ const stat = await fs . promises . stat ( filePath ) ;
24
+ statCache . set ( filePath , stat ) ;
25
+ return stat ;
26
+ }
27
+
12
28
export class FileExplorerProvider implements vscode . TreeDataProvider < FileItem > {
13
29
private _onDidChangeTreeData : vscode . EventEmitter < FileItem | undefined | null | void > =
14
30
new vscode . EventEmitter < FileItem | undefined | null | void > ( ) ;
@@ -357,7 +373,7 @@ export class FileExplorerProvider implements vscode.TreeDataProvider<FileItem> {
357
373
. filter ( file => ! this . isExcluded ( path . join ( dir , file ) ) )
358
374
. map ( async file => {
359
375
const filePath = path . join ( dir , file ) ;
360
- const stat = await fs . promises . stat ( filePath ) ;
376
+ const stat = await getStat ( filePath ) ;
361
377
const isDirectory = stat . isDirectory ( ) ;
362
378
const isSelected = this . isSelected ( filePath ) ;
363
379
const tokenCount = isDirectory
@@ -397,11 +413,21 @@ export class FileExplorerProvider implements vscode.TreeDataProvider<FileItem> {
397
413
}
398
414
399
415
private async addFile ( filePath : string ) {
416
+ const stat = await getStat ( filePath ) ;
417
+
418
+ // Check if we have a cached result for this file with the same modification time:
419
+ const cached = fileProcessCache . get ( filePath ) ;
420
+ if ( cached && cached . mtime === stat . mtimeMs ) {
421
+ // Use the cached token count.
422
+ this . _selected . set ( filePath , cached . tokenCount ) ;
423
+ return ;
424
+ }
425
+
426
+ // Read the file contents:
400
427
const buffer = await fs . promises . readFile ( filePath ) ;
401
428
402
429
const detection = jschardet . detect ( buffer ) || { } ;
403
430
let encoding = detection . encoding ? detection . encoding . toLowerCase ( ) : 'utf-8' ;
404
-
405
431
if ( encoding === 'ascii' ) {
406
432
encoding = 'utf-8' ;
407
433
} else if ( encoding === 'utf-16' || encoding === 'utf-16le' ) {
@@ -417,16 +443,23 @@ export class FileExplorerProvider implements vscode.TreeDataProvider<FileItem> {
417
443
418
444
const config = vscode . workspace . getConfiguration ( 'contextPrompt' ) ;
419
445
const minifyEnabled = config . get < boolean > ( 'minifyCode' , false ) ;
420
-
421
446
if ( minifyEnabled ) {
422
447
const language = getLanguageFromFilename ( filePath ) ;
423
448
if ( language === 'typescript' || language === 'javascript' ) {
424
449
content = await minifyCode ( content ) ;
425
450
}
426
451
}
427
452
453
+ // Compute token count from the content.:
428
454
const tokenCount = this . tokenizer . encode ( content ) . length ;
455
+
456
+ // Store the result in the LRU cache:
457
+ fileProcessCache . set ( filePath , { mtime : stat . mtimeMs , tokenCount } ) ;
458
+
459
+ // Mark this file as selected with its token count:
429
460
this . _selected . set ( filePath , tokenCount ) ;
461
+
462
+ // Update parent directories as needed:
430
463
await this . updateParentDirectorySelection ( filePath ) ;
431
464
}
432
465
@@ -450,7 +483,7 @@ export class FileExplorerProvider implements vscode.TreeDataProvider<FileItem> {
450
483
continue ;
451
484
}
452
485
453
- const stat = await fs . promises . stat ( filePath ) ;
486
+ const stat = await getStat ( filePath ) ;
454
487
if ( stat . isDirectory ( ) ) {
455
488
const { files, dirs } = await this . getAllChildren ( filePath , token ) ;
456
489
allDirs . push ( filePath , ...dirs ) ;
0 commit comments