|
6 | 6 |
|
7 | 7 |
|
8 | 8 | import { equals as arraysEqual, binarySearch2 } from '../../../../../base/common/arrays.js';
|
9 |
| -import { equals as objectsEqual } from '../../../../../base/common/objects.js'; |
10 | 9 | import { findLast } from '../../../../../base/common/arraysFind.js';
|
11 | 10 | import { Iterable } from '../../../../../base/common/iterator.js';
|
12 | 11 | import { DisposableStore } from '../../../../../base/common/lifecycle.js';
|
13 | 12 | import { ResourceMap } from '../../../../../base/common/map.js';
|
| 13 | +import { equals as objectsEqual } from '../../../../../base/common/objects.js'; |
14 | 14 | import { derived, derivedOpts, IObservable, ITransaction, ObservablePromise, observableValue, transaction } from '../../../../../base/common/observable.js';
|
15 | 15 | import { isEqual } from '../../../../../base/common/resources.js';
|
16 | 16 | import { URI } from '../../../../../base/common/uri.js';
|
@@ -338,29 +338,7 @@ export class ChatEditingTimeline {
|
338 | 338 |
|
339 | 339 | }
|
340 | 340 | const ignoreTrimWhitespace = this._ignoreTrimWhitespaceObservable.read(reader);
|
341 |
| - const promise = this._editorWorkerService.computeDiff( |
342 |
| - refs[0].object.textEditorModel.uri, |
343 |
| - refs[1].object.textEditorModel.uri, |
344 |
| - { ignoreTrimWhitespace, computeMoves: false, maxComputationTimeMs: 3000 }, |
345 |
| - 'advanced' |
346 |
| - ).then((diff): IEditSessionEntryDiff => { |
347 |
| - const entryDiff: IEditSessionEntryDiff = { |
348 |
| - originalURI: refs[0].object.textEditorModel.uri, |
349 |
| - modifiedURI: refs[1].object.textEditorModel.uri, |
350 |
| - identical: !!diff?.identical, |
351 |
| - quitEarly: !diff || diff.quitEarly, |
352 |
| - added: 0, |
353 |
| - removed: 0, |
354 |
| - }; |
355 |
| - if (diff) { |
356 |
| - for (const change of diff.changes) { |
357 |
| - entryDiff.removed += change.original.endLineNumberExclusive - change.original.startLineNumber; |
358 |
| - entryDiff.added += change.modified.endLineNumberExclusive - change.modified.startLineNumber; |
359 |
| - } |
360 |
| - } |
361 |
| - |
362 |
| - return entryDiff; |
363 |
| - }); |
| 341 | + const promise = this._computeDiff(refs[0].object.textEditorModel.uri, refs[1].object.textEditorModel.uri, ignoreTrimWhitespace); |
364 | 342 |
|
365 | 343 | return new ObservablePromise(promise);
|
366 | 344 | });
|
@@ -418,6 +396,91 @@ export class ChatEditingTimeline {
|
418 | 396 | return observable;
|
419 | 397 | }
|
420 | 398 | }
|
| 399 | + |
| 400 | + public getEntryDiffBetweenRequests(uri: URI, startRequestId: string, stopRequestId: string): IObservable<IEditSessionEntryDiff | undefined> { |
| 401 | + const snapshotUris = derivedOpts<[URI | undefined, URI | undefined]>( |
| 402 | + { equalsFn: (a, b) => arraysEqual(a, b, isEqual) }, |
| 403 | + reader => { |
| 404 | + const history = this._linearHistory.read(reader); |
| 405 | + const firstSnapshotUri = this._getFirstSnapshotForUriAfterRequest(history, uri, startRequestId, true); |
| 406 | + const lastSnapshotUri = this._getFirstSnapshotForUriAfterRequest(history, uri, stopRequestId, false); |
| 407 | + return [firstSnapshotUri, lastSnapshotUri]; |
| 408 | + }, |
| 409 | + ); |
| 410 | + const modelRefs = derived((reader) => { |
| 411 | + const snapshots = snapshotUris.read(reader); |
| 412 | + const firstSnapshotUri = snapshots[0]; |
| 413 | + const lastSnapshotUri = snapshots[1]; |
| 414 | + if (!firstSnapshotUri || !lastSnapshotUri) { |
| 415 | + return; |
| 416 | + } |
| 417 | + const store = new DisposableStore(); |
| 418 | + reader.store.add(store); |
| 419 | + const referencesPromise = Promise.all([firstSnapshotUri, lastSnapshotUri].map(u => this._textModelService.createModelReference(u))).then(refs => { |
| 420 | + if (store.isDisposed) { |
| 421 | + refs.forEach(ref => ref.dispose()); |
| 422 | + } else { |
| 423 | + refs.forEach(ref => store.add(ref)); |
| 424 | + } |
| 425 | + return refs; |
| 426 | + }); |
| 427 | + return new ObservablePromise(referencesPromise); |
| 428 | + }); |
| 429 | + const diff = derived((reader): ObservablePromise<IEditSessionEntryDiff> | undefined => { |
| 430 | + const references = modelRefs.read(reader)?.promiseResult.read(reader); |
| 431 | + const refs = references?.data; |
| 432 | + if (!refs) { |
| 433 | + return; |
| 434 | + } |
| 435 | + const ignoreTrimWhitespace = this._ignoreTrimWhitespaceObservable.read(reader); |
| 436 | + const promise = this._computeDiff(refs[0].object.textEditorModel.uri, refs[1].object.textEditorModel.uri, ignoreTrimWhitespace); |
| 437 | + return new ObservablePromise(promise); |
| 438 | + }); |
| 439 | + return derived(reader => { |
| 440 | + return diff.read(reader)?.promiseResult.read(reader)?.data || undefined; |
| 441 | + }); |
| 442 | + } |
| 443 | + |
| 444 | + private _computeDiff(originalUri: URI, modifiedUri: URI, ignoreTrimWhitespace: boolean): Promise<IEditSessionEntryDiff> { |
| 445 | + return this._editorWorkerService.computeDiff( |
| 446 | + originalUri, |
| 447 | + modifiedUri, |
| 448 | + { ignoreTrimWhitespace, computeMoves: false, maxComputationTimeMs: 3000 }, |
| 449 | + 'advanced' |
| 450 | + ).then((diff): IEditSessionEntryDiff => { |
| 451 | + const entryDiff: IEditSessionEntryDiff = { |
| 452 | + originalURI: originalUri, |
| 453 | + modifiedURI: modifiedUri, |
| 454 | + identical: !!diff?.identical, |
| 455 | + quitEarly: !diff || diff.quitEarly, |
| 456 | + added: 0, |
| 457 | + removed: 0, |
| 458 | + }; |
| 459 | + if (diff) { |
| 460 | + for (const change of diff.changes) { |
| 461 | + entryDiff.removed += change.original.endLineNumberExclusive - change.original.startLineNumber; |
| 462 | + entryDiff.added += change.modified.endLineNumberExclusive - change.modified.startLineNumber; |
| 463 | + } |
| 464 | + } |
| 465 | + return entryDiff; |
| 466 | + }); |
| 467 | + } |
| 468 | + |
| 469 | + private _getFirstSnapshotForUriAfterRequest(history: readonly IChatEditingSessionSnapshot[], uri: URI, requestId: string, inclusive: boolean): URI | undefined { |
| 470 | + const requestIndex = history.findIndex(s => s.requestId === requestId); |
| 471 | + if (requestIndex === -1) { return undefined; } |
| 472 | + const processedIndex = requestIndex + (inclusive ? 0 : 1); |
| 473 | + for (let i = processedIndex; i < history.length; i++) { |
| 474 | + const snapshot = history[i]; |
| 475 | + for (const stop of snapshot.stops) { |
| 476 | + const entry = stop.entries.get(uri); |
| 477 | + if (entry) { |
| 478 | + return entry.snapshotUri; |
| 479 | + } |
| 480 | + } |
| 481 | + } |
| 482 | + return uri; |
| 483 | + } |
421 | 484 | }
|
422 | 485 |
|
423 | 486 | function stopProvidesNewData(origin: IChatEditingSessionStop, target: IChatEditingSessionStop) {
|
|
0 commit comments