Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
掘金者说
vscode
提交
e54a781e
V
vscode
项目概览
掘金者说
/
vscode
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
vscode
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
e54a781e
编写于
1月 18, 2018
作者:
P
Peng Lyu
提交者:
GitHub
1月 18, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #41172 from Microsoft/rebornix/buffer-pt
buffer improvement with piece tree
上级
16a0a75f
8888586f
变更
13
展开全部
隐藏空白更改
内联
并排
Showing
13 changed file
with
4392 addition
and
25 deletion
+4392
-25
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts
.../editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts
+1695
-0
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts
...r/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts
+505
-0
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts
...n/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts
+180
-0
src/vs/editor/common/model/textModel.ts
src/vs/editor/common/model/textModel.ts
+6
-0
src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts
src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts
+77
-0
src/vs/editor/test/common/model/benchmark/bootstrap.js
src/vs/editor/test/common/model/benchmark/bootstrap.js
+6
-0
src/vs/editor/test/common/model/benchmark/entry.ts
src/vs/editor/test/common/model/benchmark/entry.ts
+9
-0
src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts
...tor/test/common/model/benchmark/modelbuilder.benchmark.ts
+34
-0
src/vs/editor/test/common/model/benchmark/operations.benchmark.ts
...ditor/test/common/model/benchmark/operations.benchmark.ts
+139
-0
src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts
...r/test/common/model/benchmark/searchNReplace.benchmark.ts
+51
-0
src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilderAuto.test.ts
.../model/linesTextBuffer/linesTextBufferBuilderAuto.test.ts
+1
-25
src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts
...t/common/model/linesTextBuffer/textBufferAutoTestUtils.ts
+170
-0
src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts
...mon/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts
+1519
-0
未找到文件。
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts
0 → 100644
浏览文件 @
e54a781e
此差异已折叠。
点击以展开。
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
import
{
Position
}
from
'
vs/editor/common/core/position
'
;
import
*
as
strings
from
'
vs/base/common/strings
'
;
import
{
IValidatedEditOperation
}
from
'
vs/editor/common/model/linesTextBuffer/linesTextBuffer
'
;
import
{
PieceTreeBase
,
StringBuffer
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase
'
;
import
{
IIdentifiedSingleEditOperation
,
EndOfLinePreference
,
ITextBuffer
,
ApplyEditsResult
,
IInternalModelContentChange
}
from
'
vs/editor/common/model
'
;
import
{
ITextSnapshot
}
from
'
vs/platform/files/common/files
'
;
export
class
PieceTreeTextBuffer
implements
ITextBuffer
{
private
_pieceTree
:
PieceTreeBase
;
private
_BOM
:
string
;
private
_EOL
:
string
;
private
_EOLLength
:
number
;
private
_mightContainRTL
:
boolean
;
private
_mightContainNonBasicASCII
:
boolean
;
constructor
(
chunks
:
StringBuffer
[],
BOM
:
string
,
eol
:
'
\r\n
'
|
'
\n
'
,
containsRTL
:
boolean
,
isBasicASCII
:
boolean
)
{
this
.
_BOM
=
BOM
;
this
.
_EOL
=
eol
;
this
.
_EOLLength
=
this
.
_EOL
.
length
;
this
.
_mightContainNonBasicASCII
=
!
isBasicASCII
;
this
.
_mightContainRTL
=
containsRTL
;
this
.
_pieceTree
=
new
PieceTreeBase
(
chunks
);
}
// #region TextBuffer
public
equals
(
other
:
ITextBuffer
):
boolean
{
if
(
!
(
other
instanceof
PieceTreeTextBuffer
))
{
return
false
;
}
if
(
this
.
_BOM
!==
other
.
_BOM
)
{
return
false
;
}
if
(
this
.
_EOL
!==
other
.
_EOL
)
{
return
false
;
}
return
this
.
_pieceTree
.
equal
(
other
.
_pieceTree
);
}
public
mightContainRTL
():
boolean
{
return
this
.
_mightContainRTL
;
}
public
mightContainNonBasicASCII
():
boolean
{
return
this
.
_mightContainNonBasicASCII
;
}
public
getBOM
():
string
{
return
this
.
_BOM
;
}
public
getEOL
():
string
{
return
this
.
_EOL
;
}
public
createSnapshot
(
preserveBOM
:
boolean
):
ITextSnapshot
{
return
this
.
_pieceTree
.
createSnapshot
(
preserveBOM
?
this
.
_BOM
:
''
);
}
public
getOffsetAt
(
lineNumber
:
number
,
column
:
number
):
number
{
return
this
.
_pieceTree
.
getOffsetAt
(
lineNumber
,
column
);
}
public
getPositionAt
(
offset
:
number
):
Position
{
return
this
.
_pieceTree
.
getPositionAt
(
offset
);
}
public
getRangeAt
(
start
:
number
,
length
:
number
):
Range
{
let
end
=
start
+
length
;
const
startPosition
=
this
.
getPositionAt
(
start
);
const
endPosition
=
this
.
getPositionAt
(
end
);
return
new
Range
(
startPosition
.
lineNumber
,
startPosition
.
column
,
endPosition
.
lineNumber
,
endPosition
.
column
);
}
public
getValueInRange
(
range
:
Range
,
eol
:
EndOfLinePreference
=
EndOfLinePreference
.
TextDefined
):
string
{
if
(
range
.
isEmpty
())
{
return
''
;
}
const
lineEnding
=
this
.
_getEndOfLine
(
eol
);
const
text
=
this
.
_pieceTree
.
getValueInRange
(
range
);
return
text
.
replace
(
/
\r\n
|
\r
|
\n
/g
,
lineEnding
);
}
public
getValueLengthInRange
(
range
:
Range
,
eol
:
EndOfLinePreference
=
EndOfLinePreference
.
TextDefined
):
number
{
if
(
range
.
isEmpty
())
{
return
0
;
}
if
(
range
.
startLineNumber
===
range
.
endLineNumber
)
{
return
(
range
.
endColumn
-
range
.
startColumn
);
}
let
startOffset
=
this
.
getOffsetAt
(
range
.
startLineNumber
,
range
.
startColumn
);
let
endOffset
=
this
.
getOffsetAt
(
range
.
endLineNumber
,
range
.
endColumn
);
return
endOffset
-
startOffset
;
}
public
getLength
():
number
{
return
this
.
_pieceTree
.
getLength
();
}
public
getLineCount
():
number
{
return
this
.
_pieceTree
.
getLineCount
();
}
public
getLinesContent
():
string
[]
{
return
this
.
_pieceTree
.
getLinesContent
();
}
public
getLineContent
(
lineNumber
:
number
):
string
{
return
this
.
_pieceTree
.
getLineContent
(
lineNumber
);
}
public
getLineCharCode
(
lineNumber
:
number
,
index
:
number
):
number
{
return
this
.
_pieceTree
.
getLineCharCode
(
lineNumber
,
index
);
}
public
getLineLength
(
lineNumber
:
number
):
number
{
if
(
lineNumber
===
this
.
getLineCount
())
{
let
startOffset
=
this
.
getOffsetAt
(
lineNumber
,
1
);
return
this
.
getLength
()
-
startOffset
;
}
return
this
.
getOffsetAt
(
lineNumber
+
1
,
1
)
-
this
.
getOffsetAt
(
lineNumber
,
1
)
-
this
.
_EOLLength
;
}
public
getLineMinColumn
(
lineNumber
:
number
):
number
{
return
1
;
}
public
getLineMaxColumn
(
lineNumber
:
number
):
number
{
return
this
.
getLineLength
(
lineNumber
)
+
1
;
}
public
getLineFirstNonWhitespaceColumn
(
lineNumber
:
number
):
number
{
const
result
=
strings
.
firstNonWhitespaceIndex
(
this
.
getLineContent
(
lineNumber
));
if
(
result
===
-
1
)
{
return
0
;
}
return
result
+
1
;
}
public
getLineLastNonWhitespaceColumn
(
lineNumber
:
number
):
number
{
const
result
=
strings
.
lastNonWhitespaceIndex
(
this
.
getLineContent
(
lineNumber
));
if
(
result
===
-
1
)
{
return
0
;
}
return
result
+
2
;
}
private
_getEndOfLine
(
eol
:
EndOfLinePreference
):
string
{
switch
(
eol
)
{
case
EndOfLinePreference
.
LF
:
return
'
\n
'
;
case
EndOfLinePreference
.
CRLF
:
return
'
\r\n
'
;
case
EndOfLinePreference
.
TextDefined
:
return
this
.
getEOL
();
}
throw
new
Error
(
'
Unknown EOL preference
'
);
}
public
setEOL
(
newEOL
:
'
\r\n
'
|
'
\n
'
):
void
{
this
.
_EOL
=
newEOL
;
this
.
_EOLLength
=
this
.
_EOL
.
length
;
this
.
_pieceTree
.
normalizeEOL
(
newEOL
);
}
public
applyEdits
(
rawOperations
:
IIdentifiedSingleEditOperation
[],
recordTrimAutoWhitespace
:
boolean
):
ApplyEditsResult
{
let
mightContainRTL
=
this
.
_mightContainRTL
;
let
mightContainNonBasicASCII
=
this
.
_mightContainNonBasicASCII
;
let
canReduceOperations
=
true
;
let
operations
:
IValidatedEditOperation
[]
=
[];
for
(
let
i
=
0
;
i
<
rawOperations
.
length
;
i
++
)
{
let
op
=
rawOperations
[
i
];
if
(
canReduceOperations
&&
op
.
_isTracked
)
{
canReduceOperations
=
false
;
}
let
validatedRange
=
op
.
range
;
if
(
!
mightContainRTL
&&
op
.
text
)
{
// check if the new inserted text contains RTL
mightContainRTL
=
strings
.
containsRTL
(
op
.
text
);
}
if
(
!
mightContainNonBasicASCII
&&
op
.
text
)
{
mightContainNonBasicASCII
=
!
strings
.
isBasicASCII
(
op
.
text
);
}
operations
[
i
]
=
{
sortIndex
:
i
,
identifier
:
op
.
identifier
,
range
:
validatedRange
,
rangeOffset
:
this
.
getOffsetAt
(
validatedRange
.
startLineNumber
,
validatedRange
.
startColumn
),
rangeLength
:
this
.
getValueLengthInRange
(
validatedRange
),
lines
:
op
.
text
?
op
.
text
.
split
(
/
\r\n
|
\r
|
\n
/
)
:
null
,
forceMoveMarkers
:
op
.
forceMoveMarkers
,
isAutoWhitespaceEdit
:
op
.
isAutoWhitespaceEdit
||
false
};
}
// Sort operations ascending
operations
.
sort
(
PieceTreeTextBuffer
.
_sortOpsAscending
);
for
(
let
i
=
0
,
count
=
operations
.
length
-
1
;
i
<
count
;
i
++
)
{
let
rangeEnd
=
operations
[
i
].
range
.
getEndPosition
();
let
nextRangeStart
=
operations
[
i
+
1
].
range
.
getStartPosition
();
if
(
nextRangeStart
.
isBefore
(
rangeEnd
))
{
// overlapping ranges
throw
new
Error
(
'
Overlapping ranges are not allowed!
'
);
}
}
if
(
canReduceOperations
)
{
operations
=
this
.
_reduceOperations
(
operations
);
}
// Delta encode operations
let
reverseRanges
=
PieceTreeTextBuffer
.
_getInverseEditRanges
(
operations
);
let
newTrimAutoWhitespaceCandidates
:
{
lineNumber
:
number
,
oldContent
:
string
}[]
=
[];
for
(
let
i
=
0
;
i
<
operations
.
length
;
i
++
)
{
let
op
=
operations
[
i
];
let
reverseRange
=
reverseRanges
[
i
];
if
(
recordTrimAutoWhitespace
&&
op
.
isAutoWhitespaceEdit
&&
op
.
range
.
isEmpty
())
{
// Record already the future line numbers that might be auto whitespace removal candidates on next edit
for
(
let
lineNumber
=
reverseRange
.
startLineNumber
;
lineNumber
<=
reverseRange
.
endLineNumber
;
lineNumber
++
)
{
let
currentLineContent
=
''
;
if
(
lineNumber
===
reverseRange
.
startLineNumber
)
{
currentLineContent
=
this
.
getLineContent
(
op
.
range
.
startLineNumber
);
if
(
strings
.
firstNonWhitespaceIndex
(
currentLineContent
)
!==
-
1
)
{
continue
;
}
}
newTrimAutoWhitespaceCandidates
.
push
({
lineNumber
:
lineNumber
,
oldContent
:
currentLineContent
});
}
}
}
let
reverseOperations
:
IIdentifiedSingleEditOperation
[]
=
[];
for
(
let
i
=
0
;
i
<
operations
.
length
;
i
++
)
{
let
op
=
operations
[
i
];
let
reverseRange
=
reverseRanges
[
i
];
reverseOperations
[
i
]
=
{
identifier
:
op
.
identifier
,
range
:
reverseRange
,
text
:
this
.
getValueInRange
(
op
.
range
),
forceMoveMarkers
:
op
.
forceMoveMarkers
};
}
this
.
_mightContainRTL
=
mightContainRTL
;
this
.
_mightContainNonBasicASCII
=
mightContainNonBasicASCII
;
const
contentChanges
=
this
.
_doApplyEdits
(
operations
);
let
trimAutoWhitespaceLineNumbers
:
number
[]
=
null
;
if
(
recordTrimAutoWhitespace
&&
newTrimAutoWhitespaceCandidates
.
length
>
0
)
{
// sort line numbers auto whitespace removal candidates for next edit descending
newTrimAutoWhitespaceCandidates
.
sort
((
a
,
b
)
=>
b
.
lineNumber
-
a
.
lineNumber
);
trimAutoWhitespaceLineNumbers
=
[];
for
(
let
i
=
0
,
len
=
newTrimAutoWhitespaceCandidates
.
length
;
i
<
len
;
i
++
)
{
let
lineNumber
=
newTrimAutoWhitespaceCandidates
[
i
].
lineNumber
;
if
(
i
>
0
&&
newTrimAutoWhitespaceCandidates
[
i
-
1
].
lineNumber
===
lineNumber
)
{
// Do not have the same line number twice
continue
;
}
let
prevContent
=
newTrimAutoWhitespaceCandidates
[
i
].
oldContent
;
let
lineContent
=
this
.
getLineContent
(
lineNumber
);
if
(
lineContent
.
length
===
0
||
lineContent
===
prevContent
||
strings
.
firstNonWhitespaceIndex
(
lineContent
)
!==
-
1
)
{
continue
;
}
trimAutoWhitespaceLineNumbers
.
push
(
lineNumber
);
}
}
return
new
ApplyEditsResult
(
reverseOperations
,
contentChanges
,
trimAutoWhitespaceLineNumbers
);
}
/**
* Transform operations such that they represent the same logic edit,
* but that they also do not cause OOM crashes.
*/
private
_reduceOperations
(
operations
:
IValidatedEditOperation
[]):
IValidatedEditOperation
[]
{
if
(
operations
.
length
<
1000
)
{
// We know from empirical testing that a thousand edits work fine regardless of their shape.
return
operations
;
}
// At one point, due to how events are emitted and how each operation is handled,
// some operations can trigger a high ammount of temporary string allocations,
// that will immediately get edited again.
// e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line
// Therefore, the strategy is to collapse all the operations into a huge single edit operation
return
[
this
.
_toSingleEditOperation
(
operations
)];
}
_toSingleEditOperation
(
operations
:
IValidatedEditOperation
[]):
IValidatedEditOperation
{
let
forceMoveMarkers
=
false
,
firstEditRange
=
operations
[
0
].
range
,
lastEditRange
=
operations
[
operations
.
length
-
1
].
range
,
entireEditRange
=
new
Range
(
firstEditRange
.
startLineNumber
,
firstEditRange
.
startColumn
,
lastEditRange
.
endLineNumber
,
lastEditRange
.
endColumn
),
lastEndLineNumber
=
firstEditRange
.
startLineNumber
,
lastEndColumn
=
firstEditRange
.
startColumn
,
result
:
string
[]
=
[];
for
(
let
i
=
0
,
len
=
operations
.
length
;
i
<
len
;
i
++
)
{
let
operation
=
operations
[
i
],
range
=
operation
.
range
;
forceMoveMarkers
=
forceMoveMarkers
||
operation
.
forceMoveMarkers
;
// (1) -- Push old text
for
(
let
lineNumber
=
lastEndLineNumber
;
lineNumber
<
range
.
startLineNumber
;
lineNumber
++
)
{
if
(
lineNumber
===
lastEndLineNumber
)
{
result
.
push
(
this
.
getLineContent
(
lineNumber
).
substring
(
lastEndColumn
-
1
));
}
else
{
result
.
push
(
'
\n
'
);
result
.
push
(
this
.
getLineContent
(
lineNumber
));
}
}
if
(
range
.
startLineNumber
===
lastEndLineNumber
)
{
result
.
push
(
this
.
getLineContent
(
range
.
startLineNumber
).
substring
(
lastEndColumn
-
1
,
range
.
startColumn
-
1
));
}
else
{
result
.
push
(
'
\n
'
);
result
.
push
(
this
.
getLineContent
(
range
.
startLineNumber
).
substring
(
0
,
range
.
startColumn
-
1
));
}
// (2) -- Push new text
if
(
operation
.
lines
)
{
for
(
let
j
=
0
,
lenJ
=
operation
.
lines
.
length
;
j
<
lenJ
;
j
++
)
{
if
(
j
!==
0
)
{
result
.
push
(
'
\n
'
);
}
result
.
push
(
operation
.
lines
[
j
]);
}
}
lastEndLineNumber
=
operation
.
range
.
endLineNumber
;
lastEndColumn
=
operation
.
range
.
endColumn
;
}
return
{
sortIndex
:
0
,
identifier
:
operations
[
0
].
identifier
,
range
:
entireEditRange
,
rangeOffset
:
this
.
getOffsetAt
(
entireEditRange
.
startLineNumber
,
entireEditRange
.
startColumn
),
rangeLength
:
this
.
getValueLengthInRange
(
entireEditRange
,
EndOfLinePreference
.
TextDefined
),
lines
:
result
.
join
(
''
).
split
(
'
\n
'
),
forceMoveMarkers
:
forceMoveMarkers
,
isAutoWhitespaceEdit
:
false
};
}
private
_doApplyEdits
(
operations
:
IValidatedEditOperation
[]):
IInternalModelContentChange
[]
{
operations
.
sort
(
PieceTreeTextBuffer
.
_sortOpsDescending
);
let
contentChanges
:
IInternalModelContentChange
[]
=
[];
// operations are from bottom to top
for
(
let
i
=
0
;
i
<
operations
.
length
;
i
++
)
{
let
op
=
operations
[
i
];
const
startLineNumber
=
op
.
range
.
startLineNumber
;
const
startColumn
=
op
.
range
.
startColumn
;
const
endLineNumber
=
op
.
range
.
endLineNumber
;
const
endColumn
=
op
.
range
.
endColumn
;
if
(
startLineNumber
===
endLineNumber
&&
startColumn
===
endColumn
&&
(
!
op
.
lines
||
op
.
lines
.
length
===
0
))
{
// no-op
continue
;
}
const
deletingLinesCnt
=
endLineNumber
-
startLineNumber
;
const
insertingLinesCnt
=
(
op
.
lines
?
op
.
lines
.
length
-
1
:
0
);
const
editingLinesCnt
=
Math
.
min
(
deletingLinesCnt
,
insertingLinesCnt
);
const
text
=
(
op
.
lines
?
op
.
lines
.
join
(
this
.
getEOL
())
:
''
);
if
(
text
)
{
// replacement
this
.
_pieceTree
.
delete
(
op
.
rangeOffset
,
op
.
rangeLength
);
this
.
_pieceTree
.
insert
(
op
.
rangeOffset
,
text
);
}
else
{
// deletion
this
.
_pieceTree
.
delete
(
op
.
rangeOffset
,
op
.
rangeLength
);
}
if
(
editingLinesCnt
<
insertingLinesCnt
)
{
let
newLinesContent
:
string
[]
=
[];
for
(
let
j
=
editingLinesCnt
+
1
;
j
<=
insertingLinesCnt
;
j
++
)
{
newLinesContent
.
push
(
op
.
lines
[
j
]);
}
newLinesContent
[
newLinesContent
.
length
-
1
]
=
this
.
getLineContent
(
startLineNumber
+
insertingLinesCnt
-
1
);
}
const
contentChangeRange
=
new
Range
(
startLineNumber
,
startColumn
,
endLineNumber
,
endColumn
);
contentChanges
.
push
({
range
:
contentChangeRange
,
rangeLength
:
op
.
rangeLength
,
text
:
text
,
lines
:
op
.
lines
,
rangeOffset
:
op
.
rangeOffset
,
forceMoveMarkers
:
op
.
forceMoveMarkers
});
}
return
contentChanges
;
}
// #endregion
// #region helper
// testing purpose.
public
getPieceTree
():
PieceTreeBase
{
return
this
.
_pieceTree
;
}
/**
* Assumes `operations` are validated and sorted ascending
*/
public
static
_getInverseEditRanges
(
operations
:
IValidatedEditOperation
[]):
Range
[]
{
let
result
:
Range
[]
=
[];
let
prevOpEndLineNumber
:
number
;
let
prevOpEndColumn
:
number
;
let
prevOp
:
IValidatedEditOperation
=
null
;
for
(
let
i
=
0
,
len
=
operations
.
length
;
i
<
len
;
i
++
)
{
let
op
=
operations
[
i
];
let
startLineNumber
:
number
;
let
startColumn
:
number
;
if
(
prevOp
)
{
if
(
prevOp
.
range
.
endLineNumber
===
op
.
range
.
startLineNumber
)
{
startLineNumber
=
prevOpEndLineNumber
;
startColumn
=
prevOpEndColumn
+
(
op
.
range
.
startColumn
-
prevOp
.
range
.
endColumn
);
}
else
{
startLineNumber
=
prevOpEndLineNumber
+
(
op
.
range
.
startLineNumber
-
prevOp
.
range
.
endLineNumber
);
startColumn
=
op
.
range
.
startColumn
;
}
}
else
{
startLineNumber
=
op
.
range
.
startLineNumber
;
startColumn
=
op
.
range
.
startColumn
;
}
let
resultRange
:
Range
;
if
(
op
.
lines
&&
op
.
lines
.
length
>
0
)
{
// the operation inserts something
let
lineCount
=
op
.
lines
.
length
;
let
firstLine
=
op
.
lines
[
0
];
let
lastLine
=
op
.
lines
[
lineCount
-
1
];
if
(
lineCount
===
1
)
{
// single line insert
resultRange
=
new
Range
(
startLineNumber
,
startColumn
,
startLineNumber
,
startColumn
+
firstLine
.
length
);
}
else
{
// multi line insert
resultRange
=
new
Range
(
startLineNumber
,
startColumn
,
startLineNumber
+
lineCount
-
1
,
lastLine
.
length
+
1
);
}
}
else
{
// There is nothing to insert
resultRange
=
new
Range
(
startLineNumber
,
startColumn
,
startLineNumber
,
startColumn
);
}
prevOpEndLineNumber
=
resultRange
.
endLineNumber
;
prevOpEndColumn
=
resultRange
.
endColumn
;
result
.
push
(
resultRange
);
prevOp
=
op
;
}
return
result
;
}
private
static
_sortOpsAscending
(
a
:
IValidatedEditOperation
,
b
:
IValidatedEditOperation
):
number
{
let
r
=
Range
.
compareRangesUsingEnds
(
a
.
range
,
b
.
range
);
if
(
r
===
0
)
{
return
a
.
sortIndex
-
b
.
sortIndex
;
}
return
r
;
}
private
static
_sortOpsDescending
(
a
:
IValidatedEditOperation
,
b
:
IValidatedEditOperation
):
number
{
let
r
=
Range
.
compareRangesUsingEnds
(
a
.
range
,
b
.
range
);
if
(
r
===
0
)
{
return
b
.
sortIndex
-
a
.
sortIndex
;
}
return
-
r
;
}
// #endregion
}
src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
*
as
strings
from
'
vs/base/common/strings
'
;
import
{
ITextBufferBuilder
,
DefaultEndOfLine
,
ITextBufferFactory
,
ITextBuffer
}
from
'
vs/editor/common/model
'
;
import
{
PieceTreeTextBuffer
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer
'
;
import
{
StringBuffer
,
createLineStarts
,
createLineStartsFast
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase
'
;
import
{
CharCode
}
from
'
vs/base/common/charCode
'
;
export
class
PieceTreeTextBufferFactory
implements
ITextBufferFactory
{
constructor
(
private
readonly
_chunks
:
StringBuffer
[],
private
readonly
_bom
:
string
,
private
readonly
_cr
:
number
,
private
readonly
_lf
:
number
,
private
readonly
_crlf
:
number
,
private
readonly
_containsRTL
:
boolean
,
private
readonly
_isBasicASCII
:
boolean
,
private
readonly
_normalizeEOL
:
boolean
)
{
}
private
_getEOL
(
defaultEOL
:
DefaultEndOfLine
):
'
\r\n
'
|
'
\n
'
{
const
totalEOLCount
=
this
.
_cr
+
this
.
_lf
+
this
.
_crlf
;
const
totalCRCount
=
this
.
_cr
+
this
.
_crlf
;
if
(
totalEOLCount
===
0
)
{
// This is an empty file or a file with precisely one line
return
(
defaultEOL
===
DefaultEndOfLine
.
LF
?
'
\n
'
:
'
\r\n
'
);
}
if
(
totalCRCount
>
totalEOLCount
/
2
)
{
// More than half of the file contains \r\n ending lines
return
'
\r\n
'
;
}
// At least one line more ends in \n
return
'
\n
'
;
}
public
create
(
defaultEOL
:
DefaultEndOfLine
):
ITextBuffer
{
const
eol
=
this
.
_getEOL
(
defaultEOL
);
let
chunks
=
this
.
_chunks
;
if
(
this
.
_normalizeEOL
&&
((
eol
===
'
\r\n
'
&&
(
this
.
_cr
>
0
||
this
.
_lf
>
0
))
||
(
eol
===
'
\n
'
&&
(
this
.
_cr
>
0
||
this
.
_crlf
>
0
)))
)
{
// Normalize pieces
for
(
let
i
=
0
,
len
=
chunks
.
length
;
i
<
len
;
i
++
)
{
let
str
=
chunks
[
i
].
buffer
.
replace
(
/
\r\n
|
\r
|
\n
/g
,
eol
);
let
newLineStart
=
createLineStartsFast
(
str
);
chunks
[
i
]
=
new
StringBuffer
(
str
,
newLineStart
);
}
}
return
new
PieceTreeTextBuffer
(
chunks
,
this
.
_bom
,
eol
,
this
.
_containsRTL
,
this
.
_isBasicASCII
);
}
public
getFirstLineText
(
lengthLimit
:
number
):
string
{
return
this
.
_chunks
[
0
].
buffer
.
substr
(
0
,
100
).
split
(
/
\r\n
|
\r
|
\n
/
)[
0
];
}
}
export
class
PieceTreeTextBufferBuilder
implements
ITextBufferBuilder
{
private
chunks
:
StringBuffer
[];
private
BOM
:
string
;
private
_hasPreviousChar
:
boolean
;
private
_previousChar
:
number
;
private
_tmpLineStarts
:
number
[];
private
cr
:
number
;
private
lf
:
number
;
private
crlf
:
number
;
private
containsRTL
:
boolean
;
private
isBasicASCII
:
boolean
;
constructor
()
{
this
.
chunks
=
[];
this
.
BOM
=
''
;
this
.
_hasPreviousChar
=
false
;
this
.
_previousChar
=
0
;
this
.
_tmpLineStarts
=
[];
this
.
cr
=
0
;
this
.
lf
=
0
;
this
.
crlf
=
0
;
this
.
containsRTL
=
false
;
this
.
isBasicASCII
=
true
;
}
public
acceptChunk
(
chunk
:
string
):
void
{
if
(
chunk
.
length
===
0
)
{
return
;
}
if
(
this
.
chunks
.
length
===
0
)
{
if
(
strings
.
startsWithUTF8BOM
(
chunk
))
{
this
.
BOM
=
strings
.
UTF8_BOM_CHARACTER
;
chunk
=
chunk
.
substr
(
1
);
}
}
const
lastChar
=
chunk
.
charCodeAt
(
chunk
.
length
-
1
);
if
(
lastChar
===
CharCode
.
CarriageReturn
||
(
lastChar
>=
0xd800
&&
lastChar
<=
0xdbff
))
{
// last character is \r or a high surrogate => keep it back
this
.
_acceptChunk1
(
chunk
.
substr
(
0
,
chunk
.
length
-
1
),
false
);
this
.
_hasPreviousChar
=
true
;
this
.
_previousChar
=
lastChar
;
}
else
{
this
.
_acceptChunk1
(
chunk
,
false
);
this
.
_hasPreviousChar
=
false
;
this
.
_previousChar
=
lastChar
;
}
}
private
_acceptChunk1
(
chunk
:
string
,
allowEmptyStrings
:
boolean
):
void
{
if
(
!
allowEmptyStrings
&&
chunk
.
length
===
0
)
{
// Nothing to do
return
;
}
if
(
this
.
_hasPreviousChar
)
{
this
.
_acceptChunk2
(
String
.
fromCharCode
(
this
.
_previousChar
)
+
chunk
);
}
else
{
this
.
_acceptChunk2
(
chunk
);
}
}
private
_acceptChunk2
(
chunk
:
string
):
void
{
const
lineStarts
=
createLineStarts
(
this
.
_tmpLineStarts
,
chunk
);
this
.
chunks
.
push
(
new
StringBuffer
(
chunk
,
lineStarts
.
lineStarts
));
this
.
cr
+=
lineStarts
.
cr
;
this
.
lf
+=
lineStarts
.
lf
;
this
.
crlf
+=
lineStarts
.
crlf
;
if
(
this
.
isBasicASCII
)
{
this
.
isBasicASCII
=
lineStarts
.
isBasicASCII
;
}
if
(
!
this
.
isBasicASCII
&&
!
this
.
containsRTL
)
{
// No need to check if is basic ASCII
this
.
containsRTL
=
strings
.
containsRTL
(
chunk
);
}
}
public
finish
(
normalizeEOL
:
boolean
=
true
):
PieceTreeTextBufferFactory
{
this
.
_finish
();
return
new
PieceTreeTextBufferFactory
(
this
.
chunks
,
this
.
BOM
,
this
.
cr
,
this
.
lf
,
this
.
crlf
,
this
.
containsRTL
,
this
.
isBasicASCII
,
normalizeEOL
);
}
private
_finish
():
void
{
if
(
this
.
chunks
.
length
===
0
)
{
this
.
_acceptChunk1
(
''
,
true
);
}
if
(
this
.
_hasPreviousChar
)
{
this
.
_hasPreviousChar
=
false
;
// recreate last chunk
let
lastChunk
=
this
.
chunks
[
this
.
chunks
.
length
-
1
];
lastChunk
.
buffer
+=
String
.
fromCharCode
(
this
.
_previousChar
);
let
newLineStarts
=
createLineStartsFast
(
lastChunk
.
buffer
);
lastChunk
.
lineStarts
=
newLineStarts
;
if
(
this
.
_previousChar
===
CharCode
.
CarriageReturn
)
{
this
.
cr
++
;
}
}
}
}
src/vs/editor/common/model/textModel.ts
浏览文件 @
e54a781e
...
...
@@ -34,11 +34,17 @@ import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelS
import
{
TPromise
}
from
'
vs/base/common/winjs.base
'
;
import
{
IStringStream
,
ITextSnapshot
}
from
'
vs/platform/files/common/files
'
;
import
{
LinesTextBufferBuilder
}
from
'
vs/editor/common/model/linesTextBuffer/linesTextBufferBuilder
'
;
import
{
PieceTreeTextBufferBuilder
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder
'
;
import
{
ChunksTextBufferBuilder
}
from
'
vs/editor/common/model/chunksTextBuffer/chunksTextBufferBuilder
'
;
// Here is the master switch for the text buffer implementation:
const
USE_PIECE_TREE_IMPLEMENTATION
=
false
;
const
USE_CHUNKS_TEXT_BUFFER
=
false
;
function
createTextBufferBuilder
()
{
if
(
USE_PIECE_TREE_IMPLEMENTATION
)
{
return
new
PieceTreeTextBufferBuilder
();
}
if
(
USE_CHUNKS_TEXT_BUFFER
)
{
return
new
ChunksTextBufferBuilder
();
}
...
...
src/vs/editor/test/common/model/benchmark/benchmarkUtils.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
ITextBufferBuilder
,
ITextBufferFactory
,
ITextBuffer
,
DefaultEndOfLine
}
from
'
vs/editor/common/model
'
;
import
{
LinesTextBufferBuilder
}
from
'
vs/editor/common/model/linesTextBuffer/linesTextBufferBuilder
'
;
import
{
PieceTreeTextBufferBuilder
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder
'
;
import
{
ChunksTextBufferBuilder
}
from
'
vs/editor/common/model/chunksTextBuffer/chunksTextBufferBuilder
'
;
export
function
doBenchmark
<
T
>
(
id
:
string
,
ts
:
T
[],
fn
:
(
t
:
T
)
=>
void
)
{
let
columns
:
string
[]
=
[
id
];
for
(
let
i
=
0
;
i
<
ts
.
length
;
i
++
)
{
var
start
=
process
.
hrtime
();
fn
(
ts
[
i
]);
var
diff
=
process
.
hrtime
(
start
);
columns
.
push
(
`
${(
diff
[
0
]
*
1000
+
diff
[
1
]
/
1000000
).
toFixed
(
3
)}
ms`
);
}
console
.
log
(
'
|
'
+
columns
.
join
(
'
\t
|
'
)
+
'
|
'
);
}
export
interface
IBenchmark
{
name
:
string
;
/**
* Before each cycle, this function will be called to create TextBufferFactory
*/
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
ITextBufferFactory
;
/**
* Before each cycle, this function will be called to do pre-work for text buffer.
* This will be called onece `buildBuffer` is finished.
*/
preCycle
:
(
textBuffer
:
ITextBuffer
)
=>
void
;
/**
* The function we are benchmarking
*/
fn
:
(
textBuffer
:
ITextBuffer
)
=>
void
;
}
export
class
BenchmarkSuite
{
name
:
string
;
iterations
:
number
;
benchmarks
:
IBenchmark
[];
constructor
(
suiteOptions
:
{
name
:
string
,
iterations
:
number
})
{
this
.
name
=
suiteOptions
.
name
;
this
.
iterations
=
suiteOptions
.
iterations
;
this
.
benchmarks
=
[];
}
add
(
benchmark
:
IBenchmark
)
{
this
.
benchmarks
.
push
(
benchmark
);
}
run
()
{
console
.
log
(
`|
${
this
.
name
}
\t|line buffer\t|piece table\t|edcore\t`
);
console
.
log
(
'
|---|---|---|---|
'
);
for
(
let
i
=
0
;
i
<
this
.
benchmarks
.
length
;
i
++
)
{
let
benchmark
=
this
.
benchmarks
[
i
];
let
columns
:
string
[]
=
[
benchmark
.
name
];
[
new
LinesTextBufferBuilder
(),
new
PieceTreeTextBufferBuilder
(),
new
ChunksTextBufferBuilder
()].
forEach
((
builder
:
ITextBufferBuilder
)
=>
{
let
timeDiffTotal
=
0.0
;
for
(
let
j
=
0
;
j
<
this
.
iterations
;
j
++
)
{
let
factory
=
benchmark
.
buildBuffer
(
builder
);
let
buffer
=
factory
.
create
(
DefaultEndOfLine
.
LF
);
benchmark
.
preCycle
(
buffer
);
var
start
=
process
.
hrtime
();
benchmark
.
fn
(
buffer
);
var
diff
=
process
.
hrtime
(
start
);
timeDiffTotal
+=
(
diff
[
0
]
*
1000
*
1000
+
diff
[
1
]
/
1000
);
}
columns
.
push
(
`
${(
timeDiffTotal
/
1000
/
this
.
iterations
).
toFixed
(
3
)}
ms`
);
});
console
.
log
(
'
|
'
+
columns
.
join
(
'
\t
|
'
)
+
'
|
'
);
}
console
.
log
(
'
\n
'
);
}
}
src/vs/editor/test/common/model/benchmark/bootstrap.js
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
require
(
'
../../../../../../bootstrap-amd
'
).
bootstrap
(
'
vs/editor/test/common/model/benchmark/entry
'
);
\ No newline at end of file
src/vs/editor/test/common/model/benchmark/entry.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
'
vs/editor/test/common/model/benchmark/modelbuilder.benchmark
'
;
import
'
vs/editor/test/common/model/benchmark/operations.benchmark
'
;
import
'
vs/editor/test/common/model/benchmark/searchNReplace.benchmark
'
;
\ No newline at end of file
src/vs/editor/test/common/model/benchmark/modelbuilder.benchmark.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
LinesTextBufferBuilder
}
from
'
vs/editor/common/model/linesTextBuffer/linesTextBufferBuilder
'
;
import
{
PieceTreeTextBufferBuilder
}
from
'
vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder
'
;
import
{
ITextBufferBuilder
}
from
'
vs/editor/common/model
'
;
import
{
generateRandomChunkWithLF
}
from
'
vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils
'
;
import
{
doBenchmark
}
from
'
vs/editor/test/common/model/benchmark/benchmarkUtils
'
;
let
linesTextBufferBuilder
=
new
LinesTextBufferBuilder
();
let
pieceTreeTextBufferBuilder
=
new
PieceTreeTextBufferBuilder
();
let
chunks
=
[];
for
(
let
i
=
0
;
i
<
100
;
i
++
)
{
chunks
.
push
(
generateRandomChunkWithLF
(
16
*
1000
,
64
*
1000
));
}
let
modelBuildBenchmark
=
function
(
id
:
string
,
builders
:
ITextBufferBuilder
[],
chunkCnt
:
number
)
{
doBenchmark
(
id
,
builders
,
builder
=>
{
for
(
let
i
=
0
,
len
=
Math
.
min
(
chunkCnt
,
chunks
.
length
);
i
<
len
;
i
++
)
{
builder
.
acceptChunk
(
chunks
[
i
]);
}
builder
.
finish
();
});
};
console
.
log
(
`|model builder\t|line buffer\t|piece table\t|`
);
console
.
log
(
'
|---|---|---|
'
);
for
(
let
i
of
[
10
,
100
])
{
modelBuildBenchmark
(
`
${
i
}
random chunks`
,
[
linesTextBufferBuilder
,
pieceTreeTextBufferBuilder
],
i
);
}
src/vs/editor/test/common/model/benchmark/operations.benchmark.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import
{
ITextBufferBuilder
,
EndOfLinePreference
}
from
'
vs/editor/common/model
'
;
import
{
BenchmarkSuite
}
from
'
vs/editor/test/common/model/benchmark/benchmarkUtils
'
;
import
{
generateRandomChunkWithLF
,
generateRandomEdits
,
generateSequentialInserts
,
getRandomInt
}
from
'
vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
let
fileSizes
=
[
1
,
1000
,
64
*
1000
,
32
*
1000
*
1000
];
let
editTypes
=
[
{
id
:
'
random edits
'
,
generateEdits
:
generateRandomEdits
},
{
id
:
'
sequential inserts
'
,
generateEdits
:
generateSequentialInserts
}
];
for
(
let
fileSize
of
fileSizes
)
{
let
chunks
=
[];
let
chunkCnt
=
Math
.
floor
(
fileSize
/
(
64
*
1000
));
if
(
chunkCnt
===
0
)
{
chunks
.
push
(
generateRandomChunkWithLF
(
fileSize
,
fileSize
));
}
else
{
let
chunk
=
generateRandomChunkWithLF
(
64
*
1000
,
64
*
1000
);
// try to avoid OOM
for
(
let
j
=
0
;
j
<
chunkCnt
;
j
++
)
{
chunks
.
push
(
Buffer
.
from
(
chunk
+
j
).
toString
());
}
}
for
(
let
editType
of
editTypes
)
{
const
edits
=
editType
.
generateEdits
(
chunks
,
1000
);
let
editsSuite
=
new
BenchmarkSuite
({
name
:
`File Size:
${
fileSize
}
Byte,
${
editType
.
id
}
`
,
iterations
:
10
});
for
(
let
i
of
[
10
,
100
,
1000
])
{
editsSuite
.
add
({
name
:
`apply
${
i
}
edits`
,
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
{
chunks
.
forEach
(
ck
=>
textBufferBuilder
.
acceptChunk
(
ck
));
return
textBufferBuilder
.
finish
();
},
preCycle
:
(
textBuffer
)
=>
{
return
textBuffer
;
},
fn
:
(
textBuffer
)
=>
{
// for line model, this loop doesn't reflect the real situation.
for
(
let
k
=
0
;
k
<
edits
.
length
&&
k
<
i
;
k
++
)
{
textBuffer
.
applyEdits
([
edits
[
k
]],
false
);
}
}
});
editsSuite
.
add
({
name
:
`Read all lines after
${
i
}
edits`
,
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
{
chunks
.
forEach
(
ck
=>
textBufferBuilder
.
acceptChunk
(
ck
));
return
textBufferBuilder
.
finish
();
},
preCycle
:
(
textBuffer
)
=>
{
for
(
let
k
=
0
;
k
<
edits
.
length
&&
k
<
i
;
k
++
)
{
textBuffer
.
applyEdits
([
edits
[
k
]],
false
);
}
return
textBuffer
;
},
fn
:
(
textBuffer
)
=>
{
for
(
let
j
=
0
,
len
=
textBuffer
.
getLineCount
();
j
<
len
;
j
++
)
{
var
str
=
textBuffer
.
getLineContent
(
j
+
1
);
let
firstChar
=
str
.
charCodeAt
(
0
);
let
lastChar
=
str
.
charCodeAt
(
str
.
length
-
1
);
firstChar
=
firstChar
-
lastChar
;
lastChar
=
firstChar
+
lastChar
;
firstChar
=
lastChar
-
firstChar
;
}
}
});
editsSuite
.
add
({
name
:
`Read 10 random windows after
${
i
}
edits`
,
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
{
chunks
.
forEach
(
ck
=>
textBufferBuilder
.
acceptChunk
(
ck
));
return
textBufferBuilder
.
finish
();
},
preCycle
:
(
textBuffer
)
=>
{
for
(
let
k
=
0
;
k
<
edits
.
length
&&
k
<
i
;
k
++
)
{
textBuffer
.
applyEdits
([
edits
[
k
]],
false
);
}
return
textBuffer
;
},
fn
:
(
textBuffer
)
=>
{
for
(
let
i
=
0
;
i
<
10
;
i
++
)
{
let
minLine
=
1
;
let
maxLine
=
textBuffer
.
getLineCount
();
let
startLine
=
getRandomInt
(
minLine
,
Math
.
max
(
minLine
,
maxLine
-
100
));
let
endLine
=
Math
.
min
(
maxLine
,
startLine
+
100
);
for
(
let
j
=
startLine
;
j
<
endLine
;
j
++
)
{
var
str
=
textBuffer
.
getLineContent
(
j
+
1
);
let
firstChar
=
str
.
charCodeAt
(
0
);
let
lastChar
=
str
.
charCodeAt
(
str
.
length
-
1
);
firstChar
=
firstChar
-
lastChar
;
lastChar
=
firstChar
+
lastChar
;
firstChar
=
lastChar
-
firstChar
;
}
}
}
});
editsSuite
.
add
({
name
:
`save file after
${
i
}
edits`
,
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
{
chunks
.
forEach
(
ck
=>
textBufferBuilder
.
acceptChunk
(
ck
));
return
textBufferBuilder
.
finish
();
},
preCycle
:
(
textBuffer
)
=>
{
for
(
let
k
=
0
;
k
<
edits
.
length
&&
k
<
i
;
k
++
)
{
textBuffer
.
applyEdits
([
edits
[
k
]],
false
);
}
return
textBuffer
;
},
fn
:
(
textBuffer
)
=>
{
const
lineCount
=
textBuffer
.
getLineCount
();
const
fullModelRange
=
new
Range
(
1
,
1
,
lineCount
,
textBuffer
.
getLineLength
(
lineCount
)
+
1
);
textBuffer
.
getValueInRange
(
fullModelRange
,
EndOfLinePreference
.
LF
);
}
});
}
editsSuite
.
run
();
}
}
\ No newline at end of file
src/vs/editor/test/common/model/benchmark/searchNReplace.benchmark.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
ITextBufferBuilder
}
from
'
vs/editor/common/model
'
;
import
{
generateRandomReplaces
,
generateRandomChunkWithLF
}
from
'
vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils
'
;
import
{
BenchmarkSuite
}
from
'
vs/editor/test/common/model/benchmark/benchmarkUtils
'
;
let
fileSizes
=
[
1
,
1000
,
64
*
1000
,
32
*
1000
*
1000
];
for
(
let
fileSize
of
fileSizes
)
{
let
chunks
=
[];
let
chunkCnt
=
Math
.
floor
(
fileSize
/
(
64
*
1000
));
if
(
chunkCnt
===
0
)
{
chunks
.
push
(
generateRandomChunkWithLF
(
fileSize
,
fileSize
));
}
else
{
let
chunk
=
generateRandomChunkWithLF
(
64
*
1000
,
64
*
1000
);
// try to avoid OOM
for
(
let
j
=
0
;
j
<
chunkCnt
;
j
++
)
{
chunks
.
push
(
Buffer
.
from
(
chunk
+
j
).
toString
());
}
}
let
replaceSuite
=
new
BenchmarkSuite
({
name
:
`File Size:
${
fileSize
}
Byte`
,
iterations
:
10
});
let
edits
=
generateRandomReplaces
(
chunks
,
500
,
5
,
10
);
for
(
let
i
of
[
10
,
100
,
500
])
{
replaceSuite
.
add
({
name
:
`replace
${
i
}
occurrences`
,
buildBuffer
:
(
textBufferBuilder
:
ITextBufferBuilder
)
=>
{
chunks
.
forEach
(
ck
=>
textBufferBuilder
.
acceptChunk
(
ck
));
return
textBufferBuilder
.
finish
();
},
preCycle
:
(
textBuffer
)
=>
{
return
textBuffer
;
},
fn
:
(
textBuffer
)
=>
{
textBuffer
.
applyEdits
(
edits
.
slice
(
0
,
i
),
false
);
}
});
}
replaceSuite
.
run
();
}
\ No newline at end of file
src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilderAuto.test.ts
浏览文件 @
e54a781e
...
...
@@ -5,7 +5,7 @@
'
use strict
'
;
import
{
testModelBuilder
}
from
'
./linesTextBufferBuilder.test
'
;
import
{
CharCode
}
from
'
vs/base/common/charCode
'
;
import
{
getRandomInt
,
getRandomEOLSequence
,
getRandomString
}
from
'
vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils
'
;
const
GENERATE_TESTS
=
false
;
...
...
@@ -21,30 +21,6 @@ suite('ModelBuilder Auto Tests', () => {
});
function
getRandomInt
(
min
:
number
,
max
:
number
):
number
{
return
Math
.
floor
(
Math
.
random
()
*
(
max
-
min
+
1
))
+
min
;
}
function
getRandomEOLSequence
():
string
{
let
rnd
=
getRandomInt
(
1
,
3
);
if
(
rnd
===
1
)
{
return
'
\n
'
;
}
if
(
rnd
===
2
)
{
return
'
\r
'
;
}
return
'
\r\n
'
;
}
function
getRandomString
(
minLength
:
number
,
maxLength
:
number
):
string
{
let
length
=
getRandomInt
(
minLength
,
maxLength
);
let
r
=
''
;
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
r
+=
String
.
fromCharCode
(
getRandomInt
(
CharCode
.
a
,
CharCode
.
z
));
}
return
r
;
}
function
generateRandomFile
():
string
{
let
lineCount
=
getRandomInt
(
1
,
10
);
let
mixedEOLSequence
=
getRandomInt
(
1
,
2
)
===
1
?
true
:
false
;
...
...
src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts
0 → 100644
浏览文件 @
e54a781e
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'
use strict
'
;
import
{
CharCode
}
from
'
vs/base/common/charCode
'
;
import
{
IIdentifiedSingleEditOperation
,
DefaultEndOfLine
,
ITextBufferBuilder
,
ITextBuffer
}
from
'
vs/editor/common/model
'
;
import
{
Range
}
from
'
vs/editor/common/core/range
'
;
export
function
getRandomInt
(
min
:
number
,
max
:
number
):
number
{
return
Math
.
floor
(
Math
.
random
()
*
(
max
-
min
+
1
))
+
min
;
}
export
function
getRandomEOLSequence
():
string
{
let
rnd
=
getRandomInt
(
1
,
3
);
if
(
rnd
===
1
)
{
return
'
\n
'
;
}
if
(
rnd
===
2
)
{
return
'
\r
'
;
}
return
'
\r\n
'
;
}
export
function
getRandomString
(
minLength
:
number
,
maxLength
:
number
):
string
{
let
length
=
getRandomInt
(
minLength
,
maxLength
);
let
r
=
''
;
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
r
+=
String
.
fromCharCode
(
getRandomInt
(
CharCode
.
a
,
CharCode
.
z
));
}
return
r
;
}
export
function
generateRandomEdits
(
chunks
:
string
[],
editCnt
:
number
):
IIdentifiedSingleEditOperation
[]
{
let
lines
=
[];
for
(
let
i
=
0
;
i
<
chunks
.
length
;
i
++
)
{
let
newLines
=
chunks
[
i
].
split
(
/
\r\n
|
\r
|
\n
/
);
if
(
lines
.
length
===
0
)
{
lines
.
push
(...
newLines
);
}
else
{
newLines
[
0
]
=
lines
[
lines
.
length
-
1
]
+
newLines
[
0
];
lines
.
splice
(
lines
.
length
-
1
,
1
,
...
newLines
);
}
}
let
ops
:
IIdentifiedSingleEditOperation
[]
=
[];
for
(
let
i
=
0
;
i
<
editCnt
;
i
++
)
{
let
line
=
getRandomInt
(
1
,
lines
.
length
);
let
startColumn
=
getRandomInt
(
1
,
Math
.
max
(
lines
[
line
-
1
].
length
,
1
));
let
endColumn
=
getRandomInt
(
startColumn
,
Math
.
max
(
lines
[
line
-
1
].
length
,
startColumn
));
let
text
:
string
=
''
;
if
(
Math
.
random
()
<
.
5
)
{
text
=
getRandomString
(
5
,
10
);
}
ops
.
push
({
text
:
text
,
range
:
new
Range
(
line
,
startColumn
,
line
,
endColumn
)
});
lines
[
line
-
1
]
=
lines
[
line
-
1
].
substring
(
0
,
startColumn
-
1
)
+
text
+
lines
[
line
-
1
].
substring
(
endColumn
-
1
);
}
return
ops
;
}
export
function
generateSequentialInserts
(
chunks
:
string
[],
editCnt
:
number
):
IIdentifiedSingleEditOperation
[]
{
let
lines
=
[];
for
(
let
i
=
0
;
i
<
chunks
.
length
;
i
++
)
{
let
newLines
=
chunks
[
i
].
split
(
/
\r\n
|
\r
|
\n
/
);
if
(
lines
.
length
===
0
)
{
lines
.
push
(...
newLines
);
}
else
{
newLines
[
0
]
=
lines
[
lines
.
length
-
1
]
+
newLines
[
0
];
lines
.
splice
(
lines
.
length
-
1
,
1
,
...
newLines
);
}
}
let
ops
:
IIdentifiedSingleEditOperation
[]
=
[];
for
(
let
i
=
0
;
i
<
editCnt
;
i
++
)
{
let
line
=
lines
.
length
;
let
column
=
lines
[
line
-
1
].
length
+
1
;
let
text
:
string
=
''
;
if
(
Math
.
random
()
<
.
5
)
{
text
=
'
\n
'
;
lines
.
push
(
''
);
}
else
{
text
=
getRandomString
(
1
,
2
);
lines
[
line
-
1
]
+=
text
;
}
ops
.
push
({
text
:
text
,
range
:
new
Range
(
line
,
column
,
line
,
column
)
});
}
return
ops
;
}
export
function
generateRandomReplaces
(
chunks
:
string
[],
editCnt
:
number
,
searchStringLen
:
number
,
replaceStringLen
:
number
):
IIdentifiedSingleEditOperation
[]
{
let
lines
=
[];
for
(
let
i
=
0
;
i
<
chunks
.
length
;
i
++
)
{
let
newLines
=
chunks
[
i
].
split
(
/
\r\n
|
\r
|
\n
/
);
if
(
lines
.
length
===
0
)
{
lines
.
push
(...
newLines
);
}
else
{
newLines
[
0
]
=
lines
[
lines
.
length
-
1
]
+
newLines
[
0
];
lines
.
splice
(
lines
.
length
-
1
,
1
,
...
newLines
);
}
}
let
ops
:
IIdentifiedSingleEditOperation
[]
=
[];
let
chunkSize
=
Math
.
max
(
1
,
Math
.
floor
(
lines
.
length
/
editCnt
));
let
chunkCnt
=
Math
.
floor
(
lines
.
length
/
chunkSize
);
let
replaceString
=
getRandomString
(
replaceStringLen
,
replaceStringLen
);
let
previousChunksLength
=
0
;
for
(
let
i
=
0
;
i
<
chunkCnt
;
i
++
)
{
let
startLine
=
previousChunksLength
+
1
;
let
endLine
=
previousChunksLength
+
chunkSize
;
let
line
=
getRandomInt
(
startLine
,
endLine
);
let
maxColumn
=
lines
[
line
-
1
].
length
+
1
;
let
startColumn
=
getRandomInt
(
1
,
maxColumn
);
let
endColumn
=
Math
.
min
(
maxColumn
,
startColumn
+
searchStringLen
);
ops
.
push
({
text
:
replaceString
,
range
:
new
Range
(
line
,
startColumn
,
line
,
endColumn
)
});
previousChunksLength
=
endLine
;
}
return
ops
;
}
export
function
createMockText
(
lineCount
:
number
,
minColumn
:
number
,
maxColumn
:
number
)
{
let
fixedEOL
=
getRandomEOLSequence
();
let
lines
:
string
[]
=
[];
for
(
let
i
=
0
;
i
<
lineCount
;
i
++
)
{
if
(
i
!==
0
)
{
lines
.
push
(
fixedEOL
);
}
lines
.
push
(
getRandomString
(
minColumn
,
maxColumn
));
}
return
lines
.
join
(
''
);
}
export
function
createMockBuffer
(
str
:
string
,
bufferBuilder
:
ITextBufferBuilder
):
ITextBuffer
{
bufferBuilder
.
acceptChunk
(
str
);
let
bufferFactory
=
bufferBuilder
.
finish
();
let
buffer
=
bufferFactory
.
create
(
DefaultEndOfLine
.
LF
);
return
buffer
;
}
export
function
generateRandomChunkWithLF
(
minLength
:
number
,
maxLength
:
number
):
string
{
let
length
=
getRandomInt
(
minLength
,
maxLength
);
let
r
=
''
;
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
let
randomI
=
getRandomInt
(
0
,
CharCode
.
z
-
CharCode
.
a
+
1
);
if
(
randomI
===
0
&&
Math
.
random
()
<
0.3
)
{
r
+=
'
\n
'
;
}
else
{
r
+=
String
.
fromCharCode
(
randomI
+
CharCode
.
a
-
1
);
}
}
return
r
;
}
\ No newline at end of file
src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts
0 → 100644
浏览文件 @
e54a781e
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录