Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
40b6ac6b
R
roslyn
项目概览
lwm1986
/
roslyn
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
roslyn
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
40b6ac6b
编写于
11月 03, 2016
作者:
M
Matt Warren
提交者:
GitHub
11月 03, 2016
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #13446 from mattwar/LongChanges
Long changes
上级
21328955
6adecec1
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
432 addition
and
18 deletion
+432
-18
src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs
src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs
+165
-0
src/Compilers/Core/Portable/Text/ChangedText.cs
src/Compilers/Core/Portable/Text/ChangedText.cs
+267
-18
未找到文件。
src/Compilers/Core/CodeAnalysisTest/Text/TextChangeTests.cs
浏览文件 @
40b6ac6b
...
...
@@ -620,5 +620,170 @@ public void TestEmptyChangeAfterChange2()
Assert
.
Same
(
change1
,
change2
);
// this was a no-op and returned the same instance
}
[
Fact
]
public
void
TestMergeChanges_Overlapping_NewInsideOld
()
{
var
original
=
SourceText
.
From
(
"Hello World"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"Cruel "
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
7
,
3
),
"oo"
));
Assert
.
Equal
(
"Hello Cool World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
6
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"Cool "
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_Overlapping_OldInsideNew
()
{
var
original
=
SourceText
.
From
(
"Hello World"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"Cruel "
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
2
,
14
),
"ar"
));
Assert
.
Equal
(
"Heard"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
2
,
8
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"ar"
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_Overlapping_NewBeforeOld
()
{
var
original
=
SourceText
.
From
(
"Hello World"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"Cruel "
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
6
),
" Bel"
));
Assert
.
Equal
(
"Hell Bell World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
2
),
changes
[
0
].
Span
);
Assert
.
Equal
(
" Bell "
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_Overlapping_OldBeforeNew
()
{
var
original
=
SourceText
.
From
(
"Hello World"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"Cruel "
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
7
,
6
),
"wazy V"
));
Assert
.
Equal
(
"Hello Cwazy Vorld"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
6
,
1
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"Cwazy V"
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_AfterAdjacent
()
{
var
original
=
SourceText
.
From
(
"Hell"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
"o "
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"World"
));
Assert
.
Equal
(
"Hello World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"o World"
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_AfterSeparated
()
{
var
original
=
SourceText
.
From
(
"Hell "
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
"o"
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"World"
));
Assert
.
Equal
(
"Hello World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
2
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"o"
,
changes
[
0
].
NewText
);
Assert
.
Equal
(
new
TextSpan
(
5
,
0
),
changes
[
1
].
Span
);
Assert
.
Equal
(
"World"
,
changes
[
1
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_BeforeSeparated
()
{
var
original
=
SourceText
.
From
(
"Hell Word"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
8
,
0
),
"l"
));
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
"o"
));
Assert
.
Equal
(
"Hello World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
2
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"o"
,
changes
[
0
].
NewText
);
Assert
.
Equal
(
new
TextSpan
(
8
,
0
),
changes
[
1
].
Span
);
Assert
.
Equal
(
"l"
,
changes
[
1
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_BeforeAdjacent
()
{
var
original
=
SourceText
.
From
(
"Hell"
);
var
change1
=
original
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
" World"
));
Assert
.
Equal
(
"Hell World"
,
change1
.
ToString
());
var
change2
=
change1
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
"o"
));
Assert
.
Equal
(
"Hello World"
,
change2
.
ToString
());
var
changes
=
change2
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"o World"
,
changes
[
0
].
NewText
);
}
[
Fact
]
public
void
TestMergeChanges_NoMiddleMan
()
{
var
original
=
SourceText
.
From
(
"Hell"
);
var
final
=
GetChangesWithoutMiddle
(
original
,
c
=>
c
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
4
,
0
),
"o "
)),
c
=>
c
.
WithChanges
(
new
TextChange
(
new
TextSpan
(
6
,
0
),
"World"
)));
Assert
.
Equal
(
"Hello World"
,
final
.
ToString
());
var
changes
=
final
.
GetTextChanges
(
original
);
Assert
.
Equal
(
1
,
changes
.
Count
);
Assert
.
Equal
(
new
TextSpan
(
4
,
0
),
changes
[
0
].
Span
);
Assert
.
Equal
(
"o World"
,
changes
[
0
].
NewText
);
}
private
SourceText
GetChangesWithoutMiddle
(
SourceText
original
,
Func
<
SourceText
,
SourceText
>
fnChange1
,
Func
<
SourceText
,
SourceText
>
fnChange2
)
{
WeakReference
change1
;
SourceText
change2
;
GetChangesWithoutMiddle_Helper
(
original
,
fnChange1
,
fnChange2
,
out
change1
,
out
change2
);
while
(
change1
.
IsAlive
)
{
GC
.
Collect
(
2
);
GC
.
WaitForFullGCComplete
();
}
return
change2
;
}
private
void
GetChangesWithoutMiddle_Helper
(
SourceText
original
,
Func
<
SourceText
,
SourceText
>
fnChange1
,
Func
<
SourceText
,
SourceText
>
fnChange2
,
out
WeakReference
change1
,
out
SourceText
change2
)
{
var
c1
=
fnChange1
(
original
);
change1
=
new
WeakReference
(
c1
);
change2
=
fnChange2
(
c1
);
}
}
}
\ No newline at end of file
src/Compilers/Core/Portable/Text/ChangedText.cs
浏览文件 @
40b6ac6b
...
...
@@ -4,18 +4,16 @@
using
System.Collections.Generic
;
using
System.Collections.Immutable
;
using
System.Diagnostics
;
using
System.Runtime.CompilerServices
;
using
System.Text
;
using
System.Threading
;
namespace
Microsoft.CodeAnalysis.Text
{
internal
sealed
class
ChangedText
:
SourceText
{
private
readonly
SourceText
_newText
;
// store old text weakly so we don't form unwanted chains of old texts (especially chains of ChangedTexts)
// It is only used to identify the old text in GetChangeRanges which only returns the changes if old text matches identity.
private
readonly
WeakReference
<
SourceText
>
_weakOldText
;
private
readonly
ImmutableArray
<
TextChangeRange
>
_changes
;
private
readonly
ChangeInfo
_info
;
public
ChangedText
(
SourceText
oldText
,
SourceText
newText
,
ImmutableArray
<
TextChangeRange
>
changeRanges
)
:
base
(
checksumAlgorithm
:
oldText
.
ChecksumAlgorithm
)
...
...
@@ -27,8 +25,50 @@ public ChangedText(SourceText oldText, SourceText newText, ImmutableArray<TextCh
Debug
.
Assert
(!
changeRanges
.
IsDefault
);
_newText
=
newText
;
_weakOldText
=
new
WeakReference
<
SourceText
>(
oldText
);
_changes
=
changeRanges
;
_info
=
new
ChangeInfo
(
changeRanges
,
new
WeakReference
<
SourceText
>(
oldText
),
(
oldText
as
ChangedText
)?.
_info
);
}
private
class
ChangeInfo
{
public
ImmutableArray
<
TextChangeRange
>
ChangeRanges
{
get
;
}
// store old text weakly so we don't form unwanted chains of old texts (especially chains of ChangedTexts)
// used to identify the changes in GetChangeRanges.
public
WeakReference
<
SourceText
>
WeakOldText
{
get
;
}
public
ChangeInfo
Previous
{
get
;
private
set
;
}
public
ChangeInfo
(
ImmutableArray
<
TextChangeRange
>
changeRanges
,
WeakReference
<
SourceText
>
weakOldText
,
ChangeInfo
previous
)
{
this
.
ChangeRanges
=
changeRanges
;
this
.
WeakOldText
=
weakOldText
;
this
.
Previous
=
previous
;
Clean
();
}
// clean up
private
void
Clean
()
{
// look for last info in the chain that still has reference to old text
ChangeInfo
lastInfo
=
this
;
for
(
var
info
=
this
;
info
!=
null
;
info
=
info
.
Previous
)
{
SourceText
tmp
;
if
(
info
.
WeakOldText
.
TryGetTarget
(
out
tmp
))
{
lastInfo
=
info
;
}
}
// break chain for any info's beyond that so they get GC'd
ChangeInfo
prev
;
while
(
lastInfo
!=
null
)
{
prev
=
lastInfo
.
Previous
;
lastInfo
.
Previous
=
null
;
lastInfo
=
prev
;
}
}
}
public
override
Encoding
Encoding
...
...
@@ -38,7 +78,7 @@ public override Encoding Encoding
public
IEnumerable
<
TextChangeRange
>
Changes
{
get
{
return
_
ch
anges
;
}
get
{
return
_
info
.
ChangeR
anges
;
}
}
public
override
int
Length
...
...
@@ -89,7 +129,7 @@ public override SourceText WithChanges(IEnumerable<TextChange> changes)
var
changed
=
_newText
.
WithChanges
(
changes
)
as
ChangedText
;
if
(
changed
!=
null
)
{
return
new
ChangedText
(
this
,
changed
.
_newText
,
changed
.
_
ch
anges
);
return
new
ChangedText
(
this
,
changed
.
_newText
,
changed
.
_
info
.
ChangeR
anges
);
}
else
{
...
...
@@ -110,23 +150,232 @@ public override IReadOnlyList<TextChangeRange> GetChangeRanges(SourceText oldTex
return
TextChangeRange
.
NoChanges
;
}
// try this quick check first
SourceText
actualOldText
;
if
(
_weakOldText
.
TryGetTarget
(
out
actualOldText
))
if
(
_info
.
WeakOldText
.
TryGetTarget
(
out
actualOldText
)
&&
actualOldText
==
oldText
)
{
// the supplied old text is the one we directly reference, so the changes must be the ones we have.
return
_info
.
ChangeRanges
;
}
// otherwise look to see if there are a series of changes from the old text to this text and merge them.
if
(
IsChangedFrom
(
oldText
))
{
var
changes
=
GetChangesBetween
(
oldText
,
this
);
if
(
changes
.
Count
>
1
)
{
return
Merge
(
changes
);
}
}
// the SourceText subtype for editor snapshots knows when two snapshots from the same buffer have the same contents
if
(
actualOldText
!=
null
&&
actualOldText
.
GetChangeRanges
(
oldText
).
Count
==
0
)
{
// the texts are different instances, but the contents are considered to be the same.
return
_info
.
ChangeRanges
;
}
return
ImmutableArray
.
Create
(
new
TextChangeRange
(
new
TextSpan
(
0
,
oldText
.
Length
),
_newText
.
Length
));
}
private
bool
IsChangedFrom
(
SourceText
oldText
)
{
for
(
var
info
=
_info
;
info
!=
null
;
info
=
info
.
Previous
)
{
SourceText
text
;
if
(
info
.
WeakOldText
.
TryGetTarget
(
out
text
)
&&
text
==
oldText
)
{
return
true
;
}
}
return
false
;
}
private
static
IReadOnlyList
<
ImmutableArray
<
TextChangeRange
>>
GetChangesBetween
(
SourceText
oldText
,
ChangedText
newText
)
{
var
list
=
new
List
<
ImmutableArray
<
TextChangeRange
>>();
var
change
=
newText
.
_info
;
list
.
Add
(
change
.
ChangeRanges
);
while
(
change
!=
null
)
{
SourceText
actualOldText
;
change
.
WeakOldText
.
TryGetTarget
(
out
actualOldText
);
if
(
actualOldText
==
oldText
)
{
// same identity, so the changes must be the ones we have.
return
_changes
;
return
list
;
}
if
(
actualOldText
.
GetChangeRanges
(
oldText
).
Count
==
0
)
change
=
change
.
Previous
;
if
(
change
!=
null
)
{
// the bases are different instances, but the contents are considered to be the same.
return
_changes
;
list
.
Insert
(
0
,
change
.
ChangeRanges
);
}
}
return
ImmutableArray
.
Create
(
new
TextChangeRange
(
new
TextSpan
(
0
,
oldText
.
Length
),
_newText
.
Length
));
// did not find old text, so not connected?
list
.
Clear
();
return
list
;
}
private
static
ImmutableArray
<
TextChangeRange
>
Merge
(
IReadOnlyList
<
ImmutableArray
<
TextChangeRange
>>
changeSets
)
{
Debug
.
Assert
(
changeSets
.
Count
>
1
);
var
merged
=
changeSets
[
0
];
for
(
int
i
=
1
;
i
<
changeSets
.
Count
;
i
++)
{
merged
=
Merge
(
merged
,
changeSets
[
i
]);
}
return
merged
;
}
private
static
ImmutableArray
<
TextChangeRange
>
Merge
(
ImmutableArray
<
TextChangeRange
>
oldChanges
,
ImmutableArray
<
TextChangeRange
>
newChanges
)
{
var
list
=
new
List
<
TextChangeRange
>(
oldChanges
.
Length
+
newChanges
.
Length
);
int
oldIndex
=
0
;
int
newIndex
=
0
;
int
oldDelta
=
0
;
nextNewChange
:
if
(
newIndex
<
newChanges
.
Length
)
{
var
newChange
=
newChanges
[
newIndex
];
nextOldChange
:
if
(
oldIndex
<
oldChanges
.
Length
)
{
var
oldChange
=
oldChanges
[
oldIndex
];
tryAgain
:
if
(
oldChange
.
Span
.
Length
==
0
&&
oldChange
.
NewLength
==
0
)
{
// old change is a non-change, just ignore it and move on
oldIndex
++;
goto
nextOldChange
;
}
else
if
(
newChange
.
Span
.
Length
==
0
&&
newChange
.
NewLength
==
0
)
{
// new change is a non-change, just ignore it and move on
newIndex
++;
goto
nextNewChange
;
}
else
if
(
newChange
.
Span
.
End
<
(
oldChange
.
Span
.
Start
+
oldDelta
))
{
// new change occurs entirely before old change
var
adjustedNewChange
=
new
TextChangeRange
(
new
TextSpan
(
newChange
.
Span
.
Start
-
oldDelta
,
newChange
.
Span
.
Length
),
newChange
.
NewLength
);
AddRange
(
list
,
adjustedNewChange
);
newIndex
++;
goto
nextNewChange
;
}
else
if
(
newChange
.
Span
.
Start
>
oldChange
.
Span
.
Start
+
oldDelta
+
oldChange
.
NewLength
)
{
// new change occurs entirely after old change
AddRange
(
list
,
oldChange
);
oldDelta
=
oldDelta
-
oldChange
.
Span
.
Length
+
oldChange
.
NewLength
;
oldIndex
++;
goto
nextOldChange
;
}
else
if
(
newChange
.
Span
.
Start
<
oldChange
.
Span
.
Start
+
oldDelta
)
{
// new change starts before old change, but overlaps
// add as much of new change deletion as possible and try again
var
newChangeLeadingDeletion
=
(
oldChange
.
Span
.
Start
+
oldDelta
)
-
newChange
.
Span
.
Start
;
AddRange
(
list
,
new
TextChangeRange
(
new
TextSpan
(
newChange
.
Span
.
Start
-
oldDelta
,
newChangeLeadingDeletion
),
0
));
newChange
=
new
TextChangeRange
(
new
TextSpan
(
oldChange
.
Span
.
Start
+
oldDelta
,
newChange
.
Span
.
Length
-
newChangeLeadingDeletion
),
newChange
.
NewLength
);
goto
tryAgain
;
}
else
if
(
newChange
.
Span
.
Start
>
oldChange
.
Span
.
Start
+
oldDelta
)
{
// new change starts after old change, but overlaps
// add as much of the old change as possible and try again
var
oldChangeLeadingInsertion
=
newChange
.
Span
.
Start
-
(
oldChange
.
Span
.
Start
+
oldDelta
);
AddRange
(
list
,
new
TextChangeRange
(
oldChange
.
Span
,
oldChangeLeadingInsertion
));
oldDelta
=
oldDelta
-
oldChange
.
Span
.
Length
+
oldChangeLeadingInsertion
;
oldChange
=
new
TextChangeRange
(
new
TextSpan
(
oldChange
.
Span
.
Start
,
0
),
oldChange
.
NewLength
-
oldChangeLeadingInsertion
);
newChange
=
new
TextChangeRange
(
new
TextSpan
(
oldChange
.
Span
.
Start
+
oldDelta
,
newChange
.
Span
.
Length
),
newChange
.
NewLength
);
goto
tryAgain
;
}
else
if
(
newChange
.
Span
.
Start
==
oldChange
.
Span
.
Start
+
oldDelta
)
{
// new change and old change start at same position
if
(
oldChange
.
NewLength
==
0
)
{
// old change is just a deletion, go ahead and old change now and deal with new change separately
AddRange
(
list
,
oldChange
);
oldDelta
=
oldDelta
-
oldChange
.
Span
.
Length
+
oldChange
.
NewLength
;
oldIndex
++;
goto
nextOldChange
;
}
else
if
(
newChange
.
Span
.
Length
==
0
)
{
// new change is just an insertion, go ahead and tack it on with old change
AddRange
(
list
,
new
TextChangeRange
(
oldChange
.
Span
,
oldChange
.
NewLength
+
newChange
.
NewLength
));
oldDelta
=
oldDelta
-
oldChange
.
Span
.
Length
+
oldChange
.
NewLength
;
oldIndex
++;
newIndex
++;
goto
nextNewChange
;
}
else
{
// delete as much from old change as new change can
// a new change deletion is a reduction in the old change insertion
var
oldChangeReduction
=
Math
.
Min
(
oldChange
.
NewLength
,
newChange
.
Span
.
Length
);
AddRange
(
list
,
new
TextChangeRange
(
oldChange
.
Span
,
oldChange
.
NewLength
-
oldChangeReduction
));
oldDelta
=
oldDelta
-
oldChange
.
Span
.
Length
+
(
oldChange
.
NewLength
-
oldChangeReduction
);
oldIndex
++;
// deduct the amount removed from oldChange from newChange's deletion span (since its already been applied)
newChange
=
new
TextChangeRange
(
new
TextSpan
(
oldChange
.
Span
.
Start
+
oldDelta
,
newChange
.
Span
.
Length
-
oldChangeReduction
),
newChange
.
NewLength
);
goto
nextOldChange
;
}
}
}
else
{
// no more old changes, just add adjusted new change
var
adjustedNewChange
=
new
TextChangeRange
(
new
TextSpan
(
newChange
.
Span
.
Start
-
oldDelta
,
newChange
.
Span
.
Length
),
newChange
.
NewLength
);
AddRange
(
list
,
adjustedNewChange
);
newIndex
++;
goto
nextNewChange
;
}
}
else
{
// no more new changes, just add remaining old changes
while
(
oldIndex
<
oldChanges
.
Length
)
{
AddRange
(
list
,
oldChanges
[
oldIndex
]);
oldIndex
++;
}
}
return
list
.
ToImmutableArray
();
}
private
static
void
AddRange
(
List
<
TextChangeRange
>
list
,
TextChangeRange
range
)
{
if
(
list
.
Count
>
0
)
{
var
last
=
list
[
list
.
Count
-
1
];
if
(
last
.
Span
.
End
==
range
.
Span
.
Start
)
{
// merge changes together if they are adjacent
list
[
list
.
Count
-
1
]
=
new
TextChangeRange
(
new
TextSpan
(
last
.
Span
.
Start
,
last
.
Span
.
Length
+
range
.
Span
.
Length
),
last
.
NewLength
+
range
.
NewLength
);
return
;
}
else
{
Debug
.
Assert
(
range
.
Span
.
Start
>
last
.
Span
.
End
);
}
}
list
.
Add
(
range
);
}
/// <summary>
...
...
@@ -137,7 +386,7 @@ protected override TextLineCollection GetLinesCore()
SourceText
oldText
;
TextLineCollection
oldLineInfo
;
if
(!
_
w
eakOldText
.
TryGetTarget
(
out
oldText
)
||
!
oldText
.
TryGetLines
(
out
oldLineInfo
))
if
(!
_
info
.
W
eakOldText
.
TryGetTarget
(
out
oldText
)
||
!
oldText
.
TryGetLines
(
out
oldLineInfo
))
{
// no old line starts? do it the hard way.
return
base
.
GetLinesCore
();
...
...
@@ -156,7 +405,7 @@ protected override TextLineCollection GetLinesCore()
// true if last segment ends with CR and we need to check for CR+LF code below assumes that both CR and LF are also line breaks alone
var
endsWithCR
=
false
;
foreach
(
var
change
in
_
ch
anges
)
foreach
(
var
change
in
_
info
.
ChangeR
anges
)
{
// include existing line starts that occur before this change
if
(
change
.
Span
.
Start
>
position
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录