Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
48b4e0cb
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,发现更多精彩内容 >>
提交
48b4e0cb
编写于
8月 19, 2015
作者:
C
Charles Stoner
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Copy current line if no selection
上级
ab05f362
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
189 addition
and
57 deletion
+189
-57
src/InteractiveWindow/Editor/InteractiveWindow.cs
src/InteractiveWindow/Editor/InteractiveWindow.cs
+150
-54
src/InteractiveWindow/Editor/InteractiveWindow_UIThread.cs
src/InteractiveWindow/Editor/InteractiveWindow_UIThread.cs
+2
-2
src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs
src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs
+37
-1
未找到文件。
src/InteractiveWindow/Editor/InteractiveWindow.cs
浏览文件 @
48b4e0cb
...
...
@@ -746,7 +746,26 @@ private void CutOrDelete(IEnumerable<SnapshotSpan> projectionSpans, bool isCut)
/// </summary>
private
void
CopySelection
()
{
var
spans
=
_textView
.
Selection
.
SelectedSpans
;
var
spans
=
GetSelectionSpans
(
_textView
);
var
data
=
Copy
(
spans
);
Clipboard
.
SetDataObject
(
data
,
true
);
}
private
static
NormalizedSnapshotSpanCollection
GetSelectionSpans
(
ITextView
textView
)
{
var
selection
=
textView
.
Selection
;
if
(
selection
.
IsEmpty
)
{
// Use the current line as the selection.
var
snapshotLine
=
textView
.
Caret
.
Position
.
VirtualBufferPosition
.
Position
.
GetContainingLine
();
var
span
=
new
SnapshotSpan
(
snapshotLine
.
Start
,
snapshotLine
.
LengthIncludingLineBreak
);
return
new
NormalizedSnapshotSpanCollection
(
span
);
}
return
selection
.
SelectedSpans
;
}
private
DataObject
Copy
(
NormalizedSnapshotSpanCollection
spans
)
{
var
text
=
spans
.
Aggregate
(
new
StringBuilder
(),
GetTextWithoutPrompts
,
b
=>
b
.
ToString
());
var
rtf
=
_rtfBuilderService
.
GenerateRtf
(
spans
,
_textView
);
var
data
=
new
DataObject
();
...
...
@@ -754,26 +773,36 @@ private void CopySelection()
data
.
SetData
(
DataFormats
.
Text
,
text
);
data
.
SetData
(
DataFormats
.
UnicodeText
,
text
);
data
.
SetData
(
DataFormats
.
Rtf
,
rtf
);
Clipboard
.
SetDataObject
(
data
,
true
)
;
return
data
;
}
private
StringBuilder
GetTextWithoutPrompts
(
StringBuilder
builder
,
SnapshotSpan
span
)
{
// Find the range of source spans that cover the span.
var
sourceSpans
=
GetSourceSpans
(
span
.
Snapshot
);
int
startIndex
=
GetSourceSpanIndex
(
sourceSpans
,
span
.
Start
);
int
endIndex
=
GetSourceSpanIndex
(
sourceSpans
,
span
.
End
);
Debug
.
Assert
(
startIndex
>=
0
);
Debug
.
Assert
(
endIndex
>=
startIndex
);
int
n
=
sourceSpans
.
Count
;
int
index
=
GetSourceSpanIndex
(
sourceSpans
,
span
.
Start
);
if
(
index
==
n
)
{
index
--;
}
// Add the text for all non-prompt spans within th
at
range.
for
(
int
i
=
startIndex
;
i
<=
endIndex
;
i
++)
// Add the text for all non-prompt spans within th
e
range.
for
(
;
index
<
n
;
index
++)
{
var
sourceSpan
=
sourceSpans
[
i
];
var
sourceSpan
=
sourceSpans
[
index
];
if
(
sourceSpan
.
IsEmpty
)
{
continue
;
}
var
sourceSnapshot
=
sourceSpan
.
Snapshot
;
var
mappedSpans
=
_textView
.
BufferGraph
.
MapDownToBuffer
(
span
,
SpanTrackingMode
.
EdgeExclusive
,
sourceSnapshot
.
TextBuffer
);
if
(
mappedSpans
.
Count
==
0
)
{
break
;
}
if
(!
IsPrompt
(
sourceSnapshot
))
{
var
mappedSpans
=
_textView
.
BufferGraph
.
MapDownToBuffer
(
span
,
SpanTrackingMode
.
EdgeExclusive
,
sourceSnapshot
.
TextBuffer
);
foreach
(
var
mappedSpan
in
mappedSpans
)
{
var
intersection
=
sourceSpan
.
Span
.
Intersection
(
mappedSpan
);
...
...
@@ -1570,6 +1599,10 @@ private static ReadOnlyCollection<SnapshotSpan> GetSourceSpans(ITextSnapshot sna
private
int
GetPromptIndexForPoint
(
ReadOnlyCollection
<
SnapshotSpan
>
sourceSpans
,
SnapshotPoint
point
)
{
int
index
=
GetSourceSpanIndex
(
sourceSpans
,
point
);
if
(
index
==
sourceSpans
.
Count
)
{
index
--;
}
// Find the nearest preceding prompt.
while
(!
IsPrompt
(
sourceSpans
[
index
].
Snapshot
))
{
...
...
@@ -1578,68 +1611,82 @@ private int GetPromptIndexForPoint(ReadOnlyCollection<SnapshotSpan> sourceSpans,
return
index
;
}
/// <summary>
/// Return the index of the span containing the point. Returns the
/// length of the collection if the point is at the end of the last span.
/// </summary>
private
int
GetSourceSpanIndex
(
ReadOnlyCollection
<
SnapshotSpan
>
sourceSpans
,
SnapshotPoint
point
)
{
int
index
=
BinarySearch
(
sourceSpans
,
sourceSpan
=>
{
var
start
=
_textView
.
BufferGraph
.
MapUpToBuffer
(
sourceSpan
.
Start
,
PointTrackingMode
.
Positive
,
PositionAffinity
.
Successor
,
_projectionBuffer
);
Debug
.
Assert
(
start
!=
null
);
if
(
point
<
start
.
Value
)
{
return
-
1
;
}
var
end
=
_textView
.
BufferGraph
.
MapUpToBuffer
(
sourceSpan
.
End
,
PointTrackingMode
.
Positive
,
PositionAffinity
.
Successor
,
_projectionBuffer
);
Debug
.
Assert
(
end
!=
null
);
return
(
point
<
end
.
Value
)
?
0
:
1
;
});
if
(
index
<
0
)
{
Debug
.
Assert
(~
index
==
sourceSpans
.
Count
);
Debug
.
Assert
(
point
.
Position
==
point
.
Snapshot
.
Length
);
index
=
~
index
-
1
;
}
Debug
.
Assert
(
index
>=
0
);
Debug
.
Assert
(
index
<
sourceSpans
.
Count
);
return
index
;
}
private
static
int
BinarySearch
<
T
>(
ReadOnlyCollection
<
T
>
collection
,
Func
<
T
,
int
>
compare
)
{
if
(
collection
.
Count
==
0
)
{
return
~
0
;
}
int
low
=
0
;
int
high
=
collection
.
Count
-
1
;
while
(
true
)
int
high
=
sourceSpans
.
Count
;
while
(
low
<
high
)
{
int
mid
=
low
+
(
high
-
low
)
/
2
;
int
value
=
compare
(
collection
[
mid
]
);
int
value
=
CompareToSpan
(
_textView
,
sourceSpans
,
mid
,
point
);
if
(
value
==
0
)
{
return
mid
;
}
else
if
(
value
<
0
)
{
if
(
low
==
mid
)
{
return
~
mid
;
}
high
=
mid
-
1
;
}
else
{
if
(
mid
==
high
)
{
return
~(
mid
+
1
);
}
low
=
mid
+
1
;
}
}
Debug
.
Assert
(
low
>=
0
);
Debug
.
Assert
(
low
<=
sourceSpans
.
Count
);
return
low
;
}
/// <summary>
/// Returns negative value if the point is less than the span start,
/// positive if greater than or equal to the span end, and 0 otherwise.
/// </summary>
private
static
int
CompareToSpan
(
ITextView
textView
,
ReadOnlyCollection
<
SnapshotSpan
>
sourceSpans
,
int
index
,
SnapshotPoint
point
)
{
// If this span is zero-width and there are multiple projections of the
// containing snapshot in the projection buffer, MapUpToBuffer will return
// multiple (ambiguous) projection spans. To avoid that, we compare the
// point to the end point of the nearest non-zero width span instead.
int
indexToCompare
=
index
;
while
(
sourceSpans
[
indexToCompare
].
IsEmpty
)
{
if
(
indexToCompare
==
0
)
{
// Empty span at start of buffer. Point
// must be to the right of span.
return
1
;
}
indexToCompare
--;
}
var
sourceSpan
=
sourceSpans
[
indexToCompare
];
Debug
.
Assert
(
sourceSpan
.
Length
>
0
);
var
mappedSpans
=
textView
.
BufferGraph
.
MapUpToBuffer
(
sourceSpan
,
SpanTrackingMode
.
EdgeInclusive
,
textView
.
TextBuffer
);
Debug
.
Assert
(
mappedSpans
.
Count
==
1
);
var
mappedSpan
=
mappedSpans
[
0
];
Debug
.
Assert
(
mappedSpan
.
Length
==
sourceSpan
.
Length
);
if
(
indexToCompare
<
index
)
{
var
result
=
point
.
CompareTo
(
mappedSpan
.
End
);
return
(
result
==
0
)
?
1
:
result
;
}
else
{
var
result
=
point
.
CompareTo
(
mappedSpan
.
Start
);
if
(
result
<=
0
)
{
return
result
;
}
result
=
point
.
CompareTo
(
mappedSpan
.
End
);
return
(
result
<
0
)
?
0
:
1
;
}
}
/// <summary>
...
...
@@ -1810,6 +1857,8 @@ private void ProjectionBufferChanged(object sender, TextContentChangedEventArgs
{
ReplaceProjectionSpans
(
oldProjectionSpans
,
spanEdits
);
}
CheckProjectionSpans
();
}
/// <remarks>
...
...
@@ -1897,6 +1946,53 @@ private void AppendProjectionSpans(ITrackingSpan span1, ITrackingSpan span2)
_projectionBuffer
.
ReplaceSpans
(
index
,
0
,
new
[]
{
span1
,
span2
},
EditOptions
.
None
,
editTag
:
s_suppressPromptInjectionTag
);
}
// Verify spans and GetSourceSpanIndex.
[
Conditional
(
"DEBUG"
)]
private
void
CheckProjectionSpans
()
{
var
snapshot
=
_projectionBuffer
.
CurrentSnapshot
;
var
sourceSpans
=
snapshot
.
GetSourceSpans
();
int
n
=
sourceSpans
.
Count
;
// Spans should be contiguous and span the entire buffer.
int
offset
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
// Determine the index of the first non-zero width
// span starting at the same point as current span.
int
expectedIndex
=
i
;
while
(
sourceSpans
[
expectedIndex
].
IsEmpty
)
{
expectedIndex
++;
if
(
expectedIndex
==
n
)
{
break
;
}
}
// Verify GetSourceSpanIndex returns the expected
// index for the start of the span.
int
index
=
GetSourceSpanIndex
(
sourceSpans
,
new
SnapshotPoint
(
snapshot
,
offset
));
Debug
.
Assert
(
index
==
expectedIndex
);
// If this is a non-empty span, verify GetSourceSpanIndex
// returns the index for the midpoint of the span.
int
length
=
sourceSpans
[
i
].
Length
;
if
(
length
>
0
)
{
index
=
GetSourceSpanIndex
(
sourceSpans
,
new
SnapshotPoint
(
snapshot
,
offset
+
length
/
2
));
Debug
.
Assert
(
index
==
i
);
}
offset
+=
length
;
}
Debug
.
Assert
(
offset
==
snapshot
.
Length
);
if
(
n
>
0
)
{
int
index
=
GetSourceSpanIndex
(
sourceSpans
,
new
SnapshotPoint
(
snapshot
,
snapshot
.
Length
));
Debug
.
Assert
(
index
==
n
);
}
}
#endregion
#region Editor Helpers
...
...
src/InteractiveWindow/Editor/InteractiveWindow_UIThread.cs
浏览文件 @
48b4e0cb
...
...
@@ -434,8 +434,8 @@ private void AppendInput(string text)
var
snapshot
=
_window
.
_projectionBuffer
.
CurrentSnapshot
;
var
spanCount
=
snapshot
.
SpanCount
;
var
inputSpan
=
snapshot
.
GetSourceSpan
(
spanCount
-
1
);
var
kind
=
_window
.
GetSpanKind
(
inputSpan
.
Snapshot
);
Debug
.
Assert
(
kind
==
ReplSpanKind
.
Language
||
kind
==
ReplSpanKind
.
StandardInput
);
Debug
.
Assert
(
_window
.
GetSpanKind
(
inputSpan
.
Snapshot
)
==
ReplSpanKind
.
Language
||
_window
.
GetSpanKind
(
inputSpan
.
Snapshot
)
==
ReplSpanKind
.
StandardInput
);
var
buffer
=
inputSpan
.
Snapshot
.
TextBuffer
;
var
span
=
inputSpan
.
Span
;
...
...
src/InteractiveWindow/EditorTest/InteractiveWindowTests.cs
浏览文件 @
48b4e0cb
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Threading.Tasks
;
using
System.Windows
;
using
Microsoft.VisualStudio.InteractiveWindow.Commands
;
using
Microsoft.VisualStudio.Text
;
using
Microsoft.VisualStudio.Text.Editor
;
using
Microsoft.VisualStudio.Text.Projection
;
using
Moq
;
using
Roslyn.Test.Utilities
;
using
Xunit
;
...
...
@@ -660,6 +661,41 @@ public void CutInputAndOutput()
VerifyClipboardData
(
null
);
}
/// <summary>
/// When there is no selection, copy
/// should copy the current line.
/// </summary>
[
Fact
]
public
void
CopyNoSelection
()
{
Submit
(
@"s +
t"
,
@" 1
2 "
);
CopyNoSelectionAndVerify
(
0
,
7
,
"s +\r\n"
,
@"> s +\par "
);
CopyNoSelectionAndVerify
(
7
,
11
,
"\r\n"
,
@"> \par "
);
CopyNoSelectionAndVerify
(
11
,
17
,
" t\r\n"
,
@"> t\par "
);
CopyNoSelectionAndVerify
(
17
,
21
,
" 1\r\n"
,
@" 1\par "
);
CopyNoSelectionAndVerify
(
21
,
23
,
"\r\n"
,
@"\par "
);
CopyNoSelectionAndVerify
(
23
,
28
,
"2 "
,
"2 > "
);
}
private
void
CopyNoSelectionAndVerify
(
int
start
,
int
end
,
string
expectedText
,
string
expectedRtf
)
{
var
caret
=
Window
.
TextView
.
Caret
;
var
snapshot
=
Window
.
TextView
.
TextBuffer
.
CurrentSnapshot
;
for
(
int
i
=
start
;
i
<
end
;
i
++)
{
Clipboard
.
Clear
();
caret
.
MoveTo
(
new
SnapshotPoint
(
snapshot
,
i
));
Window
.
Operations
.
Copy
();
VerifyClipboardData
(
expectedText
,
expectedRtf
);
}
}
private
void
Submit
(
string
submission
,
string
output
)
{
Task
.
Run
(()
=>
Window
.
SubmitAsync
(
new
[]
{
submission
})).
PumpingWait
();
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录