Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/common/model/editableTextModel.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { IDisposable } from '../../../../base/common/lifecycle.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { ISingleEditOperation } from '../../../common/core/editOperation.js';
10
import { Range } from '../../../common/core/range.js';
11
import { EndOfLinePreference, EndOfLineSequence } from '../../../common/model.js';
12
import { MirrorTextModel } from '../../../common/model/mirrorTextModel.js';
13
import { IModelContentChangedEvent } from '../../../common/textModelEvents.js';
14
import { assertSyncedModels, testApplyEditsWithSyncedModels } from './editableTextModelTestUtils.js';
15
import { createTextModel } from '../testTextModel.js';
16
17
suite('EditorModel - EditableTextModel.applyEdits updates mightContainRTL', () => {
18
19
ensureNoDisposablesAreLeakedInTestSuite();
20
21
function testApplyEdits(original: string[], edits: ISingleEditOperation[], before: boolean, after: boolean): void {
22
const model = createTextModel(original.join('\n'));
23
model.setEOL(EndOfLineSequence.LF);
24
25
assert.strictEqual(model.mightContainRTL(), before);
26
27
model.applyEdits(edits);
28
assert.strictEqual(model.mightContainRTL(), after);
29
model.dispose();
30
}
31
32
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation {
33
return {
34
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
35
text: text.join('\n')
36
};
37
}
38
39
test('start with RTL, insert LTR', () => {
40
testApplyEdits(['Hello,\n讝讜讛讬 注讜讘讚讛 诪讘讜住住转 砖讚注转讜'], [editOp(1, 1, 1, 1, ['hello'])], true, true);
41
});
42
43
test('start with RTL, delete RTL', () => {
44
testApplyEdits(['Hello,\n讝讜讛讬 注讜讘讚讛 诪讘讜住住转 砖讚注转讜'], [editOp(1, 1, 10, 10, [''])], true, true);
45
});
46
47
test('start with RTL, insert RTL', () => {
48
testApplyEdits(['Hello,\n讝讜讛讬 注讜讘讚讛 诪讘讜住住转 砖讚注转讜'], [editOp(1, 1, 1, 1, ['賴賳丕賰 丨賯賷賯丞 賲孬亘鬲丞 賲賳匕 夭賲賳 胤賵賷賱'])], true, true);
49
});
50
51
test('start with LTR, insert LTR', () => {
52
testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['hello'])], false, false);
53
});
54
55
test('start with LTR, insert RTL 1', () => {
56
testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['賴賳丕賰 丨賯賷賯丞 賲孬亘鬲丞 賲賳匕 夭賲賳 胤賵賷賱'])], false, true);
57
});
58
59
test('start with LTR, insert RTL 2', () => {
60
testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['讝讜讛讬 注讜讘讚讛 诪讘讜住住转 砖讚注转讜'])], false, true);
61
});
62
});
63
64
65
suite('EditorModel - EditableTextModel.applyEdits updates mightContainNonBasicASCII', () => {
66
67
ensureNoDisposablesAreLeakedInTestSuite();
68
69
function testApplyEdits(original: string[], edits: ISingleEditOperation[], before: boolean, after: boolean): void {
70
const model = createTextModel(original.join('\n'));
71
model.setEOL(EndOfLineSequence.LF);
72
73
assert.strictEqual(model.mightContainNonBasicASCII(), before);
74
75
model.applyEdits(edits);
76
assert.strictEqual(model.mightContainNonBasicASCII(), after);
77
model.dispose();
78
}
79
80
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation {
81
return {
82
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
83
text: text.join('\n')
84
};
85
}
86
87
test('start with NON-ASCII, insert ASCII', () => {
88
testApplyEdits(['Hello,\nZ眉rich'], [editOp(1, 1, 1, 1, ['hello', 'second line'])], true, true);
89
});
90
91
test('start with NON-ASCII, delete NON-ASCII', () => {
92
testApplyEdits(['Hello,\nZ眉rich'], [editOp(1, 1, 10, 10, [''])], true, true);
93
});
94
95
test('start with NON-ASCII, insert NON-ASCII', () => {
96
testApplyEdits(['Hello,\nZ眉rich'], [editOp(1, 1, 1, 1, ['Z眉rich'])], true, true);
97
});
98
99
test('start with ASCII, insert ASCII', () => {
100
testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['hello', 'second line'])], false, false);
101
});
102
103
test('start with ASCII, insert NON-ASCII', () => {
104
testApplyEdits(['Hello,\nworld!'], [editOp(1, 1, 1, 1, ['Z眉rich', 'Z眉rich'])], false, true);
105
});
106
107
});
108
109
suite('EditorModel - EditableTextModel.applyEdits', () => {
110
111
ensureNoDisposablesAreLeakedInTestSuite();
112
113
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation {
114
return {
115
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
116
text: text.join('\n'),
117
forceMoveMarkers: false
118
};
119
}
120
121
test('high-low surrogates 1', () => {
122
testApplyEditsWithSyncedModels(
123
[
124
'馃摎some',
125
'very nice',
126
'text'
127
],
128
[
129
editOp(1, 2, 1, 2, ['a'])
130
],
131
[
132
'a馃摎some',
133
'very nice',
134
'text'
135
],
136
/*inputEditsAreInvalid*/true
137
);
138
});
139
test('high-low surrogates 2', () => {
140
testApplyEditsWithSyncedModels(
141
[
142
'馃摎some',
143
'very nice',
144
'text'
145
],
146
[
147
editOp(1, 2, 1, 3, ['a'])
148
],
149
[
150
'asome',
151
'very nice',
152
'text'
153
],
154
/*inputEditsAreInvalid*/true
155
);
156
});
157
test('high-low surrogates 3', () => {
158
testApplyEditsWithSyncedModels(
159
[
160
'馃摎some',
161
'very nice',
162
'text'
163
],
164
[
165
editOp(1, 1, 1, 2, ['a'])
166
],
167
[
168
'asome',
169
'very nice',
170
'text'
171
],
172
/*inputEditsAreInvalid*/true
173
);
174
});
175
test('high-low surrogates 4', () => {
176
testApplyEditsWithSyncedModels(
177
[
178
'馃摎some',
179
'very nice',
180
'text'
181
],
182
[
183
editOp(1, 1, 1, 3, ['a'])
184
],
185
[
186
'asome',
187
'very nice',
188
'text'
189
],
190
/*inputEditsAreInvalid*/true
191
);
192
});
193
194
test('Bug 19872: Undo is funky', () => {
195
testApplyEditsWithSyncedModels(
196
[
197
'something',
198
' A',
199
'',
200
' B',
201
'something else'
202
],
203
[
204
editOp(2, 1, 2, 2, ['']),
205
editOp(3, 1, 4, 2, [''])
206
],
207
[
208
'something',
209
'A',
210
'B',
211
'something else'
212
]
213
);
214
});
215
216
test('Bug 19872: Undo is funky (2)', () => {
217
testApplyEditsWithSyncedModels(
218
[
219
'something',
220
'A',
221
'B',
222
'something else'
223
],
224
[
225
editOp(2, 1, 2, 1, [' ']),
226
editOp(3, 1, 3, 1, ['', ' '])
227
],
228
[
229
'something',
230
' A',
231
'',
232
' B',
233
'something else'
234
]
235
);
236
});
237
238
test('insert empty text', () => {
239
testApplyEditsWithSyncedModels(
240
[
241
'My First Line',
242
'\t\tMy Second Line',
243
' Third Line',
244
'',
245
'1'
246
],
247
[
248
editOp(1, 1, 1, 1, [''])
249
],
250
[
251
'My First Line',
252
'\t\tMy Second Line',
253
' Third Line',
254
'',
255
'1'
256
]
257
);
258
});
259
260
test('last op is no-op', () => {
261
testApplyEditsWithSyncedModels(
262
[
263
'My First Line',
264
'\t\tMy Second Line',
265
' Third Line',
266
'',
267
'1'
268
],
269
[
270
editOp(1, 1, 1, 2, ['']),
271
editOp(4, 1, 4, 1, [''])
272
],
273
[
274
'y First Line',
275
'\t\tMy Second Line',
276
' Third Line',
277
'',
278
'1'
279
]
280
);
281
});
282
283
test('insert text without newline 1', () => {
284
testApplyEditsWithSyncedModels(
285
[
286
'My First Line',
287
'\t\tMy Second Line',
288
' Third Line',
289
'',
290
'1'
291
],
292
[
293
editOp(1, 1, 1, 1, ['foo '])
294
],
295
[
296
'foo My First Line',
297
'\t\tMy Second Line',
298
' Third Line',
299
'',
300
'1'
301
]
302
);
303
});
304
305
test('insert text without newline 2', () => {
306
testApplyEditsWithSyncedModels(
307
[
308
'My First Line',
309
'\t\tMy Second Line',
310
' Third Line',
311
'',
312
'1'
313
],
314
[
315
editOp(1, 3, 1, 3, [' foo'])
316
],
317
[
318
'My foo First Line',
319
'\t\tMy Second Line',
320
' Third Line',
321
'',
322
'1'
323
]
324
);
325
});
326
327
test('insert one newline', () => {
328
testApplyEditsWithSyncedModels(
329
[
330
'My First Line',
331
'\t\tMy Second Line',
332
' Third Line',
333
'',
334
'1'
335
],
336
[
337
editOp(1, 4, 1, 4, ['', ''])
338
],
339
[
340
'My ',
341
'First Line',
342
'\t\tMy Second Line',
343
' Third Line',
344
'',
345
'1'
346
]
347
);
348
});
349
350
test('insert text with one newline', () => {
351
testApplyEditsWithSyncedModels(
352
[
353
'My First Line',
354
'\t\tMy Second Line',
355
' Third Line',
356
'',
357
'1'
358
],
359
[
360
editOp(1, 3, 1, 3, [' new line', 'No longer'])
361
],
362
[
363
'My new line',
364
'No longer First Line',
365
'\t\tMy Second Line',
366
' Third Line',
367
'',
368
'1'
369
]
370
);
371
});
372
373
test('insert text with two newlines', () => {
374
testApplyEditsWithSyncedModels(
375
[
376
'My First Line',
377
'\t\tMy Second Line',
378
' Third Line',
379
'',
380
'1'
381
],
382
[
383
editOp(1, 3, 1, 3, [' new line', 'One more line in the middle', 'No longer'])
384
],
385
[
386
'My new line',
387
'One more line in the middle',
388
'No longer First Line',
389
'\t\tMy Second Line',
390
' Third Line',
391
'',
392
'1'
393
]
394
);
395
});
396
397
test('insert text with many newlines', () => {
398
testApplyEditsWithSyncedModels(
399
[
400
'My First Line',
401
'\t\tMy Second Line',
402
' Third Line',
403
'',
404
'1'
405
],
406
[
407
editOp(1, 3, 1, 3, ['', '', '', '', ''])
408
],
409
[
410
'My',
411
'',
412
'',
413
'',
414
' First Line',
415
'\t\tMy Second Line',
416
' Third Line',
417
'',
418
'1'
419
]
420
);
421
});
422
423
test('insert multiple newlines', () => {
424
testApplyEditsWithSyncedModels(
425
[
426
'My First Line',
427
'\t\tMy Second Line',
428
' Third Line',
429
'',
430
'1'
431
],
432
[
433
editOp(1, 3, 1, 3, ['', '', '', '', '']),
434
editOp(3, 15, 3, 15, ['a', 'b'])
435
],
436
[
437
'My',
438
'',
439
'',
440
'',
441
' First Line',
442
'\t\tMy Second Line',
443
' Third Linea',
444
'b',
445
'',
446
'1'
447
]
448
);
449
});
450
451
test('delete empty text', () => {
452
testApplyEditsWithSyncedModels(
453
[
454
'My First Line',
455
'\t\tMy Second Line',
456
' Third Line',
457
'',
458
'1'
459
],
460
[
461
editOp(1, 1, 1, 1, [''])
462
],
463
[
464
'My First Line',
465
'\t\tMy Second Line',
466
' Third Line',
467
'',
468
'1'
469
]
470
);
471
});
472
473
test('delete text from one line', () => {
474
testApplyEditsWithSyncedModels(
475
[
476
'My First Line',
477
'\t\tMy Second Line',
478
' Third Line',
479
'',
480
'1'
481
],
482
[
483
editOp(1, 1, 1, 2, [''])
484
],
485
[
486
'y First Line',
487
'\t\tMy Second Line',
488
' Third Line',
489
'',
490
'1'
491
]
492
);
493
});
494
495
test('delete text from one line 2', () => {
496
testApplyEditsWithSyncedModels(
497
[
498
'My First Line',
499
'\t\tMy Second Line',
500
' Third Line',
501
'',
502
'1'
503
],
504
[
505
editOp(1, 1, 1, 3, ['a'])
506
],
507
[
508
'a First Line',
509
'\t\tMy Second Line',
510
' Third Line',
511
'',
512
'1'
513
]
514
);
515
});
516
517
test('delete all text from a line', () => {
518
testApplyEditsWithSyncedModels(
519
[
520
'My First Line',
521
'\t\tMy Second Line',
522
' Third Line',
523
'',
524
'1'
525
],
526
[
527
editOp(1, 1, 1, 14, [''])
528
],
529
[
530
'',
531
'\t\tMy Second Line',
532
' Third Line',
533
'',
534
'1'
535
]
536
);
537
});
538
539
test('delete text from two lines', () => {
540
testApplyEditsWithSyncedModels(
541
[
542
'My First Line',
543
'\t\tMy Second Line',
544
' Third Line',
545
'',
546
'1'
547
],
548
[
549
editOp(1, 4, 2, 6, [''])
550
],
551
[
552
'My Second Line',
553
' Third Line',
554
'',
555
'1'
556
]
557
);
558
});
559
560
test('delete text from many lines', () => {
561
testApplyEditsWithSyncedModels(
562
[
563
'My First Line',
564
'\t\tMy Second Line',
565
' Third Line',
566
'',
567
'1'
568
],
569
[
570
editOp(1, 4, 3, 5, [''])
571
],
572
[
573
'My Third Line',
574
'',
575
'1'
576
]
577
);
578
});
579
580
test('delete everything', () => {
581
testApplyEditsWithSyncedModels(
582
[
583
'My First Line',
584
'\t\tMy Second Line',
585
' Third Line',
586
'',
587
'1'
588
],
589
[
590
editOp(1, 1, 5, 2, [''])
591
],
592
[
593
''
594
]
595
);
596
});
597
598
test('two unrelated edits', () => {
599
testApplyEditsWithSyncedModels(
600
[
601
'My First Line',
602
'\t\tMy Second Line',
603
' Third Line',
604
'',
605
'123'
606
],
607
[
608
editOp(2, 1, 2, 3, ['\t']),
609
editOp(3, 1, 3, 5, [''])
610
],
611
[
612
'My First Line',
613
'\tMy Second Line',
614
'Third Line',
615
'',
616
'123'
617
]
618
);
619
});
620
621
test('two edits on one line', () => {
622
testApplyEditsWithSyncedModels(
623
[
624
'\t\tfirst\t ',
625
'\t\tsecond line',
626
'\tthird line',
627
'fourth line',
628
'\t\t<!@#fifth#@!>\t\t'
629
],
630
[
631
editOp(5, 3, 5, 7, ['']),
632
editOp(5, 12, 5, 16, [''])
633
],
634
[
635
'\t\tfirst\t ',
636
'\t\tsecond line',
637
'\tthird line',
638
'fourth line',
639
'\t\tfifth\t\t'
640
]
641
);
642
});
643
644
test('many edits', () => {
645
testApplyEditsWithSyncedModels(
646
[
647
'{"x" : 1}'
648
],
649
[
650
editOp(1, 2, 1, 2, ['\n ']),
651
editOp(1, 5, 1, 6, ['']),
652
editOp(1, 9, 1, 9, ['\n'])
653
],
654
[
655
'{',
656
' "x": 1',
657
'}'
658
]
659
);
660
});
661
662
test('many edits reversed', () => {
663
testApplyEditsWithSyncedModels(
664
[
665
'{',
666
' "x": 1',
667
'}'
668
],
669
[
670
editOp(1, 2, 2, 3, ['']),
671
editOp(2, 6, 2, 6, [' ']),
672
editOp(2, 9, 3, 1, [''])
673
],
674
[
675
'{"x" : 1}'
676
]
677
);
678
});
679
680
test('replacing newlines 1', () => {
681
testApplyEditsWithSyncedModels(
682
[
683
'{',
684
'"a": true,',
685
'',
686
'"b": true',
687
'}'
688
],
689
[
690
editOp(1, 2, 2, 1, ['', '\t']),
691
editOp(2, 11, 4, 1, ['', '\t'])
692
],
693
[
694
'{',
695
'\t"a": true,',
696
'\t"b": true',
697
'}'
698
]
699
);
700
});
701
702
test('replacing newlines 2', () => {
703
testApplyEditsWithSyncedModels(
704
[
705
'some text',
706
'some more text',
707
'now comes an empty line',
708
'',
709
'after empty line',
710
'and the last line'
711
],
712
[
713
editOp(1, 5, 3, 1, [' text', 'some more text', 'some more text']),
714
editOp(3, 2, 4, 1, ['o more lines', 'asd', 'asd', 'asd']),
715
editOp(5, 1, 5, 6, ['zzzzzzzz']),
716
editOp(5, 11, 6, 16, ['1', '2', '3', '4'])
717
],
718
[
719
'some text',
720
'some more text',
721
'some more textno more lines',
722
'asd',
723
'asd',
724
'asd',
725
'zzzzzzzz empt1',
726
'2',
727
'3',
728
'4ne'
729
]
730
);
731
});
732
733
test('advanced 1', () => {
734
testApplyEditsWithSyncedModels(
735
[
736
' { "d": [',
737
' null',
738
' ] /*comment*/',
739
' ,"e": /*comment*/ [null] }',
740
],
741
[
742
editOp(1, 1, 1, 2, ['']),
743
editOp(1, 3, 1, 10, ['', ' ']),
744
editOp(1, 16, 2, 14, ['', ' ']),
745
editOp(2, 18, 3, 9, ['', ' ']),
746
editOp(3, 22, 4, 9, ['']),
747
editOp(4, 10, 4, 10, ['', ' ']),
748
editOp(4, 28, 4, 28, ['', ' ']),
749
editOp(4, 32, 4, 32, ['', ' ']),
750
editOp(4, 33, 4, 34, ['', ''])
751
],
752
[
753
'{',
754
' "d": [',
755
' null',
756
' ] /*comment*/,',
757
' "e": /*comment*/ [',
758
' null',
759
' ]',
760
'}',
761
]
762
);
763
});
764
765
test('advanced simplified', () => {
766
testApplyEditsWithSyncedModels(
767
[
768
' abc',
769
' ,def'
770
],
771
[
772
editOp(1, 1, 1, 4, ['']),
773
editOp(1, 7, 2, 2, ['']),
774
editOp(2, 3, 2, 3, ['', ''])
775
],
776
[
777
'abc,',
778
'def'
779
]
780
);
781
});
782
783
test('issue #144', () => {
784
testApplyEditsWithSyncedModels(
785
[
786
'package caddy',
787
'',
788
'func main() {',
789
'\tfmt.Println("Hello World! :)")',
790
'}',
791
''
792
],
793
[
794
editOp(1, 1, 6, 1, [
795
'package caddy',
796
'',
797
'import "fmt"',
798
'',
799
'func main() {',
800
'\tfmt.Println("Hello World! :)")',
801
'}',
802
''
803
])
804
],
805
[
806
'package caddy',
807
'',
808
'import "fmt"',
809
'',
810
'func main() {',
811
'\tfmt.Println("Hello World! :)")',
812
'}',
813
''
814
]
815
);
816
});
817
818
test('issue #2586 Replacing selected end-of-line with newline locks up the document', () => {
819
testApplyEditsWithSyncedModels(
820
[
821
'something',
822
'interesting'
823
],
824
[
825
editOp(1, 10, 2, 1, ['', ''])
826
],
827
[
828
'something',
829
'interesting'
830
]
831
);
832
});
833
834
test('issue #3980', () => {
835
testApplyEditsWithSyncedModels(
836
[
837
'class A {',
838
' someProperty = false;',
839
' someMethod() {',
840
' this.someMethod();',
841
' }',
842
'}',
843
],
844
[
845
editOp(1, 8, 1, 9, ['', '']),
846
editOp(3, 17, 3, 18, ['', '']),
847
editOp(3, 18, 3, 18, [' ']),
848
editOp(4, 5, 4, 5, [' ']),
849
],
850
[
851
'class A',
852
'{',
853
' someProperty = false;',
854
' someMethod()',
855
' {',
856
' this.someMethod();',
857
' }',
858
'}',
859
]
860
);
861
});
862
863
function testApplyEditsFails(original: string[], edits: ISingleEditOperation[]): void {
864
const model = createTextModel(original.join('\n'));
865
866
let hasThrown = false;
867
try {
868
model.applyEdits(edits);
869
} catch (err) {
870
hasThrown = true;
871
}
872
assert.ok(hasThrown, 'expected model.applyEdits to fail.');
873
874
model.dispose();
875
}
876
877
test('touching edits: two inserts at the same position', () => {
878
testApplyEditsWithSyncedModels(
879
[
880
'hello world'
881
],
882
[
883
editOp(1, 1, 1, 1, ['a']),
884
editOp(1, 1, 1, 1, ['b']),
885
],
886
[
887
'abhello world'
888
]
889
);
890
});
891
892
test('touching edits: insert and replace touching', () => {
893
testApplyEditsWithSyncedModels(
894
[
895
'hello world'
896
],
897
[
898
editOp(1, 1, 1, 1, ['b']),
899
editOp(1, 1, 1, 3, ['ab']),
900
],
901
[
902
'babllo world'
903
]
904
);
905
});
906
907
test('overlapping edits: two overlapping replaces', () => {
908
testApplyEditsFails(
909
[
910
'hello world'
911
],
912
[
913
editOp(1, 1, 1, 2, ['b']),
914
editOp(1, 1, 1, 3, ['ab']),
915
]
916
);
917
});
918
919
test('overlapping edits: two overlapping deletes', () => {
920
testApplyEditsFails(
921
[
922
'hello world'
923
],
924
[
925
editOp(1, 1, 1, 2, ['']),
926
editOp(1, 1, 1, 3, ['']),
927
]
928
);
929
});
930
931
test('touching edits: two touching replaces', () => {
932
testApplyEditsWithSyncedModels(
933
[
934
'hello world'
935
],
936
[
937
editOp(1, 1, 1, 2, ['H']),
938
editOp(1, 2, 1, 3, ['E']),
939
],
940
[
941
'HEllo world'
942
]
943
);
944
});
945
946
test('touching edits: two touching deletes', () => {
947
testApplyEditsWithSyncedModels(
948
[
949
'hello world'
950
],
951
[
952
editOp(1, 1, 1, 2, ['']),
953
editOp(1, 2, 1, 3, ['']),
954
],
955
[
956
'llo world'
957
]
958
);
959
});
960
961
test('touching edits: insert and replace', () => {
962
testApplyEditsWithSyncedModels(
963
[
964
'hello world'
965
],
966
[
967
editOp(1, 1, 1, 1, ['H']),
968
editOp(1, 1, 1, 3, ['e']),
969
],
970
[
971
'Hello world'
972
]
973
);
974
});
975
976
test('touching edits: replace and insert', () => {
977
testApplyEditsWithSyncedModels(
978
[
979
'hello world'
980
],
981
[
982
editOp(1, 1, 1, 3, ['H']),
983
editOp(1, 3, 1, 3, ['e']),
984
],
985
[
986
'Hello world'
987
]
988
);
989
});
990
991
test('change while emitting events 1', () => {
992
let disposable!: IDisposable;
993
assertSyncedModels('Hello', (model, assertMirrorModels) => {
994
model.applyEdits([{
995
range: new Range(1, 6, 1, 6),
996
text: ' world!',
997
// forceMoveMarkers: false
998
}]);
999
1000
assertMirrorModels();
1001
1002
}, (model) => {
1003
let isFirstTime = true;
1004
disposable = model.onDidChangeContent(() => {
1005
if (!isFirstTime) {
1006
return;
1007
}
1008
isFirstTime = false;
1009
1010
model.applyEdits([{
1011
range: new Range(1, 13, 1, 13),
1012
text: ' How are you?',
1013
// forceMoveMarkers: false
1014
}]);
1015
});
1016
});
1017
disposable.dispose();
1018
});
1019
1020
test('change while emitting events 2', () => {
1021
let disposable!: IDisposable;
1022
assertSyncedModels('Hello', (model, assertMirrorModels) => {
1023
model.applyEdits([{
1024
range: new Range(1, 6, 1, 6),
1025
text: ' world!',
1026
// forceMoveMarkers: false
1027
}]);
1028
1029
assertMirrorModels();
1030
1031
}, (model) => {
1032
let isFirstTime = true;
1033
disposable = model.onDidChangeContent((e: IModelContentChangedEvent) => {
1034
if (!isFirstTime) {
1035
return;
1036
}
1037
isFirstTime = false;
1038
1039
model.applyEdits([{
1040
range: new Range(1, 13, 1, 13),
1041
text: ' How are you?',
1042
// forceMoveMarkers: false
1043
}]);
1044
});
1045
});
1046
disposable.dispose();
1047
});
1048
1049
test('issue #1580: Changes in line endings are not correctly reflected in the extension host, leading to invalid offsets sent to external refactoring tools', () => {
1050
const model = createTextModel('Hello\nWorld!');
1051
assert.strictEqual(model.getEOL(), '\n');
1052
1053
const mirrorModel2 = new MirrorTextModel(null!, model.getLinesContent(), model.getEOL(), model.getVersionId());
1054
let mirrorModel2PrevVersionId = model.getVersionId();
1055
1056
const disposable = model.onDidChangeContent((e: IModelContentChangedEvent) => {
1057
const versionId = e.versionId;
1058
if (versionId < mirrorModel2PrevVersionId) {
1059
console.warn('Model version id did not advance between edits (2)');
1060
}
1061
mirrorModel2PrevVersionId = versionId;
1062
mirrorModel2.onEvents(e);
1063
});
1064
1065
const assertMirrorModels = () => {
1066
assert.strictEqual(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
1067
assert.strictEqual(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
1068
};
1069
1070
model.setEOL(EndOfLineSequence.CRLF);
1071
assertMirrorModels();
1072
1073
disposable.dispose();
1074
model.dispose();
1075
mirrorModel2.dispose();
1076
});
1077
1078
test('issue #47733: Undo mangles unicode characters', () => {
1079
const model = createTextModel('\'馃憗\'');
1080
1081
model.applyEdits([
1082
{ range: new Range(1, 1, 1, 1), text: '"' },
1083
{ range: new Range(1, 2, 1, 2), text: '"' },
1084
]);
1085
1086
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '"\'"馃憗\'');
1087
1088
assert.deepStrictEqual(model.validateRange(new Range(1, 3, 1, 4)), new Range(1, 3, 1, 4));
1089
1090
model.applyEdits([
1091
{ range: new Range(1, 1, 1, 2), text: null },
1092
{ range: new Range(1, 3, 1, 4), text: null },
1093
]);
1094
1095
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\'馃憗\'');
1096
1097
model.dispose();
1098
});
1099
1100
test('issue #48741: Broken undo stack with move lines up with multiple cursors', () => {
1101
const model = createTextModel([
1102
'line1',
1103
'line2',
1104
'line3',
1105
'',
1106
].join('\n'));
1107
1108
const undoEdits = model.applyEdits([
1109
{ range: new Range(4, 1, 4, 1), text: 'line3', },
1110
{ range: new Range(3, 1, 3, 6), text: null, },
1111
{ range: new Range(2, 1, 3, 1), text: null, },
1112
{ range: new Range(3, 6, 3, 6), text: '\nline2' }
1113
], true);
1114
1115
model.applyEdits(undoEdits);
1116
1117
assert.deepStrictEqual(model.getValue(), 'line1\nline2\nline3\n');
1118
1119
model.dispose();
1120
});
1121
});
1122
1123