@@ -14,14 +14,17 @@ import { ITextModel, ITextModelUpdateOptions } from 'vs/editor/common/model';
14
14
import { ISingleEditOperation } from 'vs/editor/common/core/editOperation' ;
15
15
import { IModelService } from 'vs/editor/common/services/model' ;
16
16
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2' ;
17
- import { IApplyEditsOptions , IEditorPropertiesChangeData , IResolvedTextEditorConfiguration , ITextEditorConfigurationUpdate , IUndoStopOptions , TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol' ;
17
+ import { IApplyEditsOptions , IEditorPropertiesChangeData , IResolvedTextEditorConfiguration , ITextEditorConfigurationUpdate , IUndoStopOptions , SetDecorationsResult , TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol' ;
18
18
import { IEditorPane } from 'vs/workbench/common/editor' ;
19
19
import { equals } from 'vs/base/common/arrays' ;
20
20
import { CodeEditorStateFlag , EditorState } from 'vs/editor/contrib/editorState/browser/editorState' ;
21
21
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService' ;
22
22
import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser' ;
23
23
import { MainThreadDocuments } from 'vs/workbench/api/browser/mainThreadDocuments' ;
24
24
import { ISnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetSession' ;
25
+ import { IModelContentChange , IModelContentChangedEvent } from 'vs/editor/common/textModelEvents' ;
26
+ import { Position } from 'vs/editor/common/core/position' ;
27
+ import { splitLines } from 'vs/base/common/strings' ;
25
28
26
29
export interface IFocusTracker {
27
30
onGainedFocus ( ) : void ;
@@ -420,34 +423,49 @@ export class MainThreadTextEditor {
420
423
}
421
424
}
422
425
423
- public setDecorations ( key : string , versionIdCheck : number , ranges : IDecorationOptions [ ] ) : boolean {
426
+ public setDecorations ( key : string , versionIdCheck : number , ranges : IDecorationOptions [ ] ) : SetDecorationsResult {
424
427
if ( ! this . _codeEditor ) {
425
- return false ;
426
- }
427
- if ( this . _model . getVersionId ( ) !== versionIdCheck ) {
428
- // throw new Error('Model has changed in the meantime!');
429
- // model changed in the meantime
430
- return false ;
428
+ return { type : 'error' } ;
431
429
}
430
+ const currentModelVersionId = this . _model . getVersionId ( ) ;
431
+ const modelChangedInMeantime = currentModelVersionId !== versionIdCheck ;
432
432
this . _codeEditor . setDecorationsByType ( 'exthost-api' , key , ranges ) ;
433
- return true ;
433
+ return (
434
+ modelChangedInMeantime
435
+ ? { type : 'warn' , versionId : currentModelVersionId }
436
+ : { type : 'ok' }
437
+ ) ;
434
438
}
435
439
436
- public setDecorationsFast ( key : string , versionIdCheck : number , _ranges : number [ ] ) : boolean {
440
+ public setDecorationsFast ( key : string , versionIdCheck : number , _ranges : number [ ] ) : SetDecorationsResult {
437
441
if ( ! this . _codeEditor ) {
438
- return false ;
439
- }
440
- if ( this . _model . getVersionId ( ) !== versionIdCheck ) {
441
- // throw new Error('Model has changed in the meantime!');
442
- // model changed in the meantime
443
- return false ;
442
+ return { type : 'error' } ;
444
443
}
444
+ const currentModelVersionId = this . _model . getVersionId ( ) ;
445
+ const modelChangedInMeantime = currentModelVersionId !== versionIdCheck ;
445
446
const ranges : Range [ ] = [ ] ;
446
447
for ( let i = 0 , len = Math . floor ( _ranges . length / 4 ) ; i < len ; i ++ ) {
447
448
ranges [ i ] = new Range ( _ranges [ 4 * i ] , _ranges [ 4 * i + 1 ] , _ranges [ 4 * i + 2 ] , _ranges [ 4 * i + 3 ] ) ;
448
449
}
449
450
this . _codeEditor . setDecorationsByTypeFast ( key , ranges ) ;
450
- return true ;
451
+ return (
452
+ modelChangedInMeantime
453
+ ? { type : 'warn' , versionId : currentModelVersionId }
454
+ : { type : 'ok' }
455
+ ) ;
456
+ }
457
+
458
+ private _createRangeTransformer ( range : Range [ ] , rangeVersionId : number ) : ( rng : Range ) Range [ ] {
459
+ const recentChangeEvents = this . _modelService . getRecentModelContentChangeEvents ( this . _model ) ;
460
+ const missedRecentChangeEvent = recentChangeEvents . filter ( change => change . versionId > rangeVersionId ) ;
461
+
462
+ // const pendingChanges = ;
463
+ // this._modelService.getRecentModelContentChangeEvents(this._model).
464
+ // if (this._model.getVersionId() === modelVersionId) {
465
+ // // the model version is still the same
466
+ // return selections;
467
+ // }
468
+ // return selections.map(selection => this._model.normalizeSelectionRange(selection));
451
469
}
452
470
453
471
public revealRange ( range : IRange , revealType : TextEditorRevealType ) : void {
@@ -560,3 +578,86 @@ export class MainThreadTextEditor {
560
578
return true ;
561
579
}
562
580
}
581
+
582
+ interface IRangeTransformer {
583
+ ( range : Range ) : Range ;
584
+ }
585
+
586
+ type CreateRecentChangesRangeTransformerResult = (
587
+ { kind : 'versionTooOld' }
588
+ | { kind : 'versionUpToDate' }
589
+ | { kind : 'transformer' , value : IRangeTransformer }
590
+ ) ;
591
+
592
+ function createRecentChangesTransformer ( modelService : IModelService , model : ITextModel , versionId : number ) : CreateRecentChangesRangeTransformerResult {
593
+ if ( model . getVersionId ( ) === versionId ) {
594
+ return { kind : 'versionUpToDate' } ;
595
+ }
596
+ const recentChangeEvents = modelService . getRecentModelContentChangeEvents ( model ) ;
597
+ const missedRecentChangeEvents = recentChangeEvents . filter ( change => change . versionId > versionId ) ;
598
+ if ( missedRecentChangeEvents . length === 0 ) {
599
+ // versionId is too old and we no longer have these recent changes
600
+ return { kind : 'versionTooOld' } ;
601
+ }
602
+ const firstChangeEvent = missedRecentChangeEvents [ 0 ] ;
603
+ if ( firstChangeEvent . versionId !== versionId + 1 ) {
604
+ // cannot compute transformer because some changes have been dropped
605
+ return { kind : 'versionTooOld' } ;
606
+ }
607
+ return { kind : 'transformer' , value : createRangeTransformer ( missedRecentChangeEvents ) } ;
608
+ }
609
+
610
+ function createRangeTransformer ( changes : IModelContentChangedEvent [ ] ) : IRangeTransformer {
611
+ return ( range : Range ) : Range => {
612
+ // let result = range;
613
+ let startPosition = range . getStartPosition ( ) ;
614
+ let endPosition = range . getEndPosition ( ) ;
615
+ for ( const change of changes ) {
616
+ for ( const innerChange of change . changes ) {
617
+ result = applyEditToRange ( result , innerChange ) ;
618
+ }
619
+ }
620
+ return result ;
621
+ } ;
622
+ }
623
+
624
+ function applyEditToRange ( range : Range , edit : IModelContentChange ) : Range {
625
+ // const [startLineNumber, startColumn] = RangeUtils.getLineNumberAndColumnFromOffset(range.startLineNumber, range.startColumn, edit.range.startLineNumber, edit.range.startColumn, edit.text);
626
+ // const [endLineNumber, endColumn] = RangeUtils.getLineNumberAndColumnFromOffset(range.endLineNumber, range.endColumn, edit.range.endLineNumber, edit.range.endColumn, edit.text);
627
+ // return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
628
+ }
629
+
630
+ //function createPositionTransformer(edit: IModelContentChange[]): (position: Position) => Position {
631
+
632
+ function convertPositionAgainstEdit ( position : Position , edit : IModelContentChange ) {
633
+ const lineNumber = position . lineNumber ;
634
+ const column = position . column ;
635
+
636
+ const editStartLineNumber = edit . range . startLineNumber ;
637
+ const editStartColumn = edit . range . startColumn ;
638
+ const editEndLineNumber = edit . range . endLineNumber ;
639
+ const editEndColumn = edit . range . endColumn ;
640
+
641
+ if ( lineNumber < editStartLineNumber || ( lineNumber === editStartLineNumber && column < editStartColumn ) ) {
642
+ // Position is before the edit range, no need to adjust
643
+ return position ;
644
+ }
645
+
646
+ if ( lineNumber > editEndLineNumber || ( lineNumber === editEndLineNumber && column >= editEndColumn ) ) {
647
+ // Position is after the edit range, adjust by the difference in length
648
+ const newLines = splitLines ( edit . text ) ;
649
+ const lineDelta = newLines . length - ( editEndLineNumber - editStartLineNumber + 1 ) ;
650
+ const columnDelta = newLines . length > 0 ? newLines [ 0 ] . length - editEndColumn + column : 0 ;
651
+ return new Position ( lineNumber + lineDelta , column + columnDelta ) ;
652
+ }
653
+
654
+ if ( lineNumber === editStartLineNumber && column >= editStartColumn ) {
655
+ // Position is at the start of the edit range, adjust by the difference in length
656
+ const newLines = splitLines ( edit . text ) ;
657
+ const columnDelta = newLines . length > 0 ? newLines [ 0 ] . length - editStartColumn + column : 0 ;
658
+ return new Position ( lineNumber , column + columnDelta ) ;
659
+ }
660
+
661
+ // Position is inside the edit range, return null to indicate that the position is deleted
662
+ return null ;
663
+ }
0 commit comments