@@ -9,11 +9,11 @@ import { assertNever } from '../../../../../base/common/assert.js';
99import { ThrottledDelayer } from '../../../../../base/common/async.js' ;
1010import { Event } from '../../../../../base/common/event.js' ;
1111import { Disposable , DisposableStore , IDisposable } from '../../../../../base/common/lifecycle.js' ;
12- import { ResourceSet } from '../../../../../base/common/map.js' ;
12+ import { mapsStrictEqualIgnoreOrder , ResourceMap , ResourceSet } from '../../../../../base/common/map.js' ;
1313import { equals as objectsEqual } from '../../../../../base/common/objects.js' ;
14- import { derived , derivedOpts , IObservable , IReader , ITransaction , ObservablePromise , observableSignalFromEvent , observableValue , observableValueOpts , transaction } from '../../../../../base/common/observable.js' ;
14+ import { constObservable , derived , derivedOpts , IObservable , IReader , ITransaction , ObservablePromise , observableSignalFromEvent , observableValue , observableValueOpts , transaction } from '../../../../../base/common/observable.js' ;
1515import { isEqual } from '../../../../../base/common/resources.js' ;
16- import { Mutable } from '../../../../../base/common/types.js' ;
16+ import { isDefined , Mutable } from '../../../../../base/common/types.js' ;
1717import { URI } from '../../../../../base/common/uri.js' ;
1818import { generateUuid } from '../../../../../base/common/uuid.js' ;
1919import { TextEdit } from '../../../../../editor/common/languages.js' ;
@@ -26,7 +26,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com
2626import { CellEditType , CellUri , INotebookTextModel } from '../../../notebook/common/notebookCommon.js' ;
2727import { INotebookEditorModelResolverService } from '../../../notebook/common/notebookEditorModelResolverService.js' ;
2828import { INotebookService } from '../../../notebook/common/notebookService.js' ;
29- import { IEditSessionEntryDiff , IModifiedEntryTelemetryInfo } from '../../common/chatEditingService.js' ;
29+ import { IEditSessionDiffStats , IEditSessionEntryDiff , IModifiedEntryTelemetryInfo } from '../../common/chatEditingService.js' ;
3030import { IChatRequestDisablement } from '../../common/chatModel.js' ;
3131import { IChatEditingCheckpointTimeline } from './chatEditingCheckpointTimeline.js' ;
3232import { FileOperation , FileOperationType , IChatEditingTimelineState , ICheckpoint , IFileBaseline , IReconstructedFileExistsState , IReconstructedFileNotExistsState , IReconstructedFileState } from './chatEditingOperations.js' ;
@@ -832,4 +832,62 @@ export class ChatEditingCheckpointTimelineImpl implements IChatEditingCheckpoint
832832 return entryDiff ;
833833 } ) ;
834834 }
835+
836+ public getDiffsForFilesInSession ( ) : IObservable < readonly IEditSessionEntryDiff [ ] > {
837+ const startEpochs = derivedOpts < ResourceMap < number > > ( { equalsFn : mapsStrictEqualIgnoreOrder } , reader => {
838+ const uris = new ResourceMap < number > ( ) ;
839+ for ( const baseline of this . _fileBaselines . values ( ) ) {
840+ uris . set ( baseline . uri , Math . min ( baseline . epoch , uris . get ( baseline . uri ) ?? Number . MAX_SAFE_INTEGER ) ) ;
841+ }
842+ for ( const operation of this . _operations . read ( reader ) ) {
843+ if ( operation . type === FileOperationType . Create ) {
844+ uris . set ( operation . uri , 0 ) ;
845+ }
846+ }
847+
848+ return uris ;
849+ } ) ;
850+
851+ // URIs are never removed from the set and we never adjust baselines backwards
852+ // (history is immutable) so we can easily cache to avoid regenerating diffs when new files are added
853+ const prevDiffs = new ResourceMap < IObservable < IEditSessionEntryDiff | undefined > > ( ) ;
854+
855+ const perFileDiffs = derived ( this , reader => {
856+ const checkpoints = this . _checkpoints . read ( reader ) ;
857+ const firstCheckpoint = checkpoints [ 0 ] ;
858+ if ( ! firstCheckpoint ) {
859+ return [ ] ;
860+ }
861+
862+ const uris = startEpochs . read ( reader ) ;
863+ const diffs : IObservable < IEditSessionEntryDiff | undefined > [ ] = [ ] ;
864+
865+ for ( const [ uri , epoch ] of uris ) {
866+ const obs = prevDiffs . get ( uri ) ?? this . _getEntryDiffBetweenEpochs ( uri ,
867+ constObservable ( { start : checkpoints . findLast ( cp => cp . epoch <= epoch ) || firstCheckpoint , end : undefined } ) ) ;
868+ prevDiffs . set ( uri , obs ) ;
869+ diffs . push ( obs ) ;
870+ }
871+
872+ return diffs ;
873+ } ) ;
874+
875+ return perFileDiffs . map ( ( diffs , reader ) => {
876+ return diffs . flatMap ( d => d . read ( reader ) ) . filter ( isDefined ) ;
877+ } ) ;
878+ }
879+
880+ public getDiffForSession ( ) : IObservable < IEditSessionDiffStats > {
881+ const fileDiffs = this . getDiffsForFilesInSession ( ) ;
882+ return derived ( reader => {
883+ const diffs = fileDiffs . read ( reader ) ;
884+ let added = 0 ;
885+ let removed = 0 ;
886+ for ( const diff of diffs ) {
887+ added += diff . added ;
888+ removed += diff . removed ;
889+ }
890+ return { added, removed } ;
891+ } ) ;
892+ }
835893}
0 commit comments