Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lwm1986
roslyn
提交
84d6a7a5
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,发现更多精彩内容 >>
提交
84d6a7a5
编写于
4月 22, 2020
作者:
D
David Barbet
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add tests and fix impl
上级
40c74504
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
170 addition
and
38 deletion
+170
-38
src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs
...ies/LanguageServer/AbstractLanguageServerProtocolTests.cs
+29
-10
src/Features/LanguageServer/Protocol/Extensions/TextEditEqualityComparer.cs
...ageServer/Protocol/Extensions/TextEditEqualityComparer.cs
+26
-0
src/Features/LanguageServer/Protocol/Handler/Rename/RenameHandler.cs
...s/LanguageServer/Protocol/Handler/Rename/RenameHandler.cs
+34
-27
src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs
...es/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs
+81
-1
未找到文件。
src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs
浏览文件 @
84d6a7a5
...
...
@@ -18,7 +18,6 @@
using
Microsoft.CodeAnalysis.LanguageServer.Handler.Commands
;
using
Microsoft.CodeAnalysis.Test.Utilities
;
using
Microsoft.CodeAnalysis.Text
;
using
Microsoft.CodeAnalysis.UnitTests
;
using
Microsoft.VisualStudio.Composition
;
using
Microsoft.VisualStudio.Text.Adornments
;
using
Newtonsoft.Json
;
...
...
@@ -192,26 +191,46 @@ protected Workspace CreateTestWorkspace(string[] markups, out Dictionary<string,
{
var
workspace
=
TestWorkspace
.
CreateCSharp
(
markups
,
exportProvider
:
GetExportProvider
());
var
solution
=
workspace
.
CurrentSolution
;
locations
=
new
Dictionary
<
string
,
IList
<
LSP
.
Location
>>();
foreach
(
var
document
in
workspace
.
Documents
)
{
var
text
=
solution
.
GetDocument
(
document
.
Id
).
GetTextSynchronously
(
CancellationToken
.
None
);
foreach
(
var
(
name
,
spans
)
in
document
.
AnnotatedSpans
)
{
locations
.
GetOrAdd
(
name
,
_
=>
new
List
<
LSP
.
Location
>())
.
AddRange
(
spans
.
Select
(
span
=>
ProtocolConversions
.
TextSpanToLocation
(
span
,
text
,
new
Uri
(
GetDocumentFilePathFromName
(
document
.
Name
)))));
}
solution
=
solution
.
WithDocumentFilePath
(
document
.
Id
,
GetDocumentFilePathFromName
(
document
.
Name
));
}
workspace
.
ChangeSolution
(
solution
);
locations
=
GetAnnotatedLocations
(
workspace
,
solution
);
return
workspace
;
}
protected
Workspace
CreateXmlTestWorkspace
(
string
xmlContent
,
out
Dictionary
<
string
,
IList
<
LSP
.
Location
>>
locations
)
{
var
workspace
=
TestWorkspace
.
Create
(
xmlContent
,
exportProvider
:
GetExportProvider
());
locations
=
GetAnnotatedLocations
(
workspace
,
workspace
.
CurrentSolution
);
return
workspace
;
}
private
Dictionary
<
string
,
IList
<
LSP
.
Location
>>
GetAnnotatedLocations
(
TestWorkspace
workspace
,
Solution
solution
)
{
var
locations
=
new
Dictionary
<
string
,
IList
<
LSP
.
Location
>>();
foreach
(
var
testDocument
in
workspace
.
Documents
)
{
var
document
=
solution
.
GetDocument
(
testDocument
.
Id
);
var
text
=
document
.
GetTextSynchronously
(
CancellationToken
.
None
);
foreach
(
var
(
name
,
spans
)
in
testDocument
.
AnnotatedSpans
)
{
var
locationsForName
=
locations
.
GetOrValue
(
name
,
new
List
<
LSP
.
Location
>());
locationsForName
.
AddRange
(
spans
.
Select
(
span
=>
ProtocolConversions
.
TextSpanToLocation
(
span
,
text
,
new
Uri
(
document
.
FilePath
))));
// Linked files will return duplicate annotated Locations for each document that links to the same file.
// Since the test output only cares about the actual file, make sure we de-dupe before returning.
locations
[
name
]
=
locationsForName
.
Distinct
().
ToList
();
}
}
return
locations
;
}
// Private protected because LanguageServerProtocol is internal
private
protected
static
LanguageServerProtocol
GetLanguageServer
(
Solution
solution
)
{
...
...
src/Features/LanguageServer/Protocol/Extensions/TextEditEqualityComparer.cs
0 → 100644
浏览文件 @
84d6a7a5
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using
System.Collections.Generic
;
using
Microsoft.VisualStudio.LanguageServer.Protocol
;
namespace
Microsoft.CodeAnalysis.LanguageServer
{
internal
class
TextEditEqualityComparer
:
IEqualityComparer
<
TextEdit
>
{
public
bool
Equals
(
TextEdit
x
,
TextEdit
y
)
{
return
EqualityComparer
<
Range
>.
Default
.
Equals
(
x
.
Range
,
y
.
Range
)
&&
x
.
NewText
==
y
.
NewText
;
}
public
int
GetHashCode
(
TextEdit
obj
)
{
var
hashCode
=
-
1114201889
;
hashCode
=
(
hashCode
*
-
1521134295
)
+
EqualityComparer
<
Range
>.
Default
.
GetHashCode
(
obj
.
Range
);
hashCode
=
(
hashCode
*
-
1521134295
)
+
EqualityComparer
<
string
>.
Default
.
GetHashCode
(
obj
.
NewText
);
return
hashCode
;
}
}
}
src/Features/LanguageServer/Protocol/Handler/Rename/RenameHandler.cs
浏览文件 @
84d6a7a5
...
...
@@ -5,36 +5,38 @@
#
nullable
enable
using
System
;
using
System.Collections.Generic
;
using
System.Composition
;
using
System.Linq
;
using
System.Text.RegularExpressions
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Microsoft.CodeAnalysis.Editor
;
using
Microsoft.CodeAnalysis.Host.Mef
;
using
Microsoft.CodeAnalysis.PooledObjects
;
using
Microsoft.CodeAnalysis.Shared.Extensions
;
using
Microsoft.VisualStudio.LanguageServer.Protocol
;
using
Roslyn.Utilities
;
using
LSP
=
Microsoft
.
VisualStudio
.
LanguageServer
.
Protocol
;
namespace
Microsoft.CodeAnalysis.LanguageServer.Handler
{
[
ExportLspMethod
(
LSP
.
Methods
.
TextDocumentRenameName
),
Shared
]
internal
class
RenameHandler
:
IRequestHandler
<
LSP
.
RenameParams
,
WorkspaceEdit
>
internal
class
RenameHandler
:
IRequestHandler
<
LSP
.
RenameParams
,
WorkspaceEdit
?
>
{
private
static
TextEditEqualityComparer
s_textEditEqualityComparer
=
new
TextEditEqualityComparer
();
[
ImportingConstructor
]
[
Obsolete
(
MefConstruction
.
ImportingConstructorMessage
,
error
:
true
)]
public
RenameHandler
()
{
}
public
async
Task
<
WorkspaceEdit
>
HandleRequestAsync
(
Solution
solution
,
RenameParams
request
,
ClientCapabilities
clientCapabilities
,
CancellationToken
cancellationToken
)
public
async
Task
<
WorkspaceEdit
?
>
HandleRequestAsync
(
Solution
solution
,
RenameParams
request
,
ClientCapabilities
clientCapabilities
,
CancellationToken
cancellationToken
)
{
WorkspaceEdit
workspaceEdit
=
null
;
WorkspaceEdit
?
workspaceEdit
=
null
;
var
document
=
solution
.
GetDocumentFromURI
(
request
.
TextDocument
.
Uri
);
if
(
document
!=
null
)
{
var
renameService
=
document
.
Project
.
LanguageServices
.
GetService
<
IEditorInlineRenameService
>();
var
renameService
=
document
.
Project
.
LanguageServices
.
Get
Required
Service
<
IEditorInlineRenameService
>();
var
position
=
await
document
.
GetPositionFromLinePositionAsync
(
ProtocolConversions
.
PositionToLinePosition
(
request
.
Position
),
cancellationToken
).
ConfigureAwait
(
false
);
var
renameInfo
=
await
renameService
.
GetRenameInfoAsync
(
document
,
position
,
cancellationToken
).
ConfigureAwait
(
false
);
...
...
@@ -49,35 +51,40 @@ public async Task<WorkspaceEdit> HandleRequestAsync(Solution solution, RenamePar
var
newSolution
=
renameReplacementInfo
.
NewSolution
;
var
solutionChanges
=
newSolution
.
GetChanges
(
solution
);
// Merge changes in linked files. Will result in linked file documents having the same changes in all linked documents.
// Once the changes are the same across linked files, we can take the distinct changes by file uri
var
solutionWithLinkedFileChangesMerged
=
newSolution
.
WithMergedLinkedFileChangesAsync
(
solution
,
solutionChanges
,
cancellationToken
:
cancellationToken
).
Result
;
solutionChanges
=
solutionWithLinkedFileChangesMerged
.
GetChanges
(
solution
);
var
changedDocuments
=
solutionChanges
.
GetProjectChanges
()
.
SelectMany
(
p
=>
p
.
GetChangedDocuments
(
onlyGetDocumentsWithTextChanges
:
true
))
.
GroupBy
(
docId
=>
newSolution
.
GetRequiredDocument
(
docId
).
FilePath
).
Select
(
group
=>
group
.
First
());
var
changedDocuments
=
solutionWithLinkedFileChangesMerged
.
GetChanges
(
solution
).
GetProjectChanges
().
SelectMany
(
p
=>
p
.
GetChangedDocuments
(
onlyGetDocumentsWithTextChanges
:
true
));
var
documentEdits
=
new
ArrayBuilder
<
TextDocumentEdit
>();
foreach
(
var
docId
in
changedDocuments
)
// Linked files will create multiple documents with duplicate edits for the same actual file.
// So take only the unique edits per URI to avoid returning duplicate edits.
var
textEdits
=
new
Dictionary
<
Uri
,
List
<
TextEdit
>>();
foreach
(
var
changedDocument
in
changedDocuments
)
{
var
oldDoc
=
solution
.
GetRequiredDocument
(
docId
);
var
newDoc
=
newSolution
.
GetRequiredDocument
(
docId
);
var
textChanges
=
await
newDoc
.
GetTextChangesAsync
(
oldDoc
,
cancellationToken
).
ConfigureAwait
(
false
);
var
oldText
=
await
oldDoc
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
var
textDocumentEdit
=
new
TextDocumentEdit
{
TextDocument
=
new
VersionedTextDocumentIdentifier
{
Uri
=
newDoc
.
GetURI
()
},
Edits
=
textChanges
.
Select
(
tc
=>
ProtocolConversions
.
TextChangeToTextEdit
(
tc
,
oldText
)).
ToArray
()
};
documentEdits
.
Add
(
textDocumentEdit
);
var
documentUri
=
newSolution
.
GetRequiredDocument
(
changedDocument
).
GetURI
();
var
textChangesForUri
=
textEdits
.
GetOrValue
(
documentUri
,
new
List
<
TextEdit
>());
textChangesForUri
.
AddRange
(
await
GetTextChangesAsync
(
changedDocument
,
newSolution
,
solution
,
cancellationToken
).
ConfigureAwait
(
false
));
textEdits
[
documentUri
]
=
textChangesForUri
.
Distinct
(
s_textEditEqualityComparer
).
ToList
();
}
workspaceEdit
=
new
WorkspaceEdit
{
DocumentChanges
=
documentEdits
.
ToArrayAndFree
()
};
var
documentEdits
=
textEdits
.
Select
(
kvp
=>
new
TextDocumentEdit
{
TextDocument
=
new
VersionedTextDocumentIdentifier
{
Uri
=
kvp
.
Key
},
Edits
=
kvp
.
Value
.
ToArray
()
}).
ToArray
();
workspaceEdit
=
new
WorkspaceEdit
{
DocumentChanges
=
documentEdits
};
}
return
workspaceEdit
;
static
async
Task
<
IEnumerable
<
TextEdit
>>
GetTextChangesAsync
(
DocumentId
documentId
,
Solution
newSolution
,
Solution
oldSolution
,
CancellationToken
cancellationToken
)
{
var
oldDoc
=
oldSolution
.
GetRequiredDocument
(
documentId
);
var
newDoc
=
newSolution
.
GetRequiredDocument
(
documentId
);
var
textChanges
=
await
newDoc
.
GetTextChangesAsync
(
oldDoc
,
cancellationToken
).
ConfigureAwait
(
false
);
var
oldText
=
await
oldDoc
.
GetTextAsync
(
cancellationToken
).
ConfigureAwait
(
false
);
return
textChanges
.
Select
(
textChange
=>
ProtocolConversions
.
TextChangeToTextEdit
(
textChange
,
oldText
));
}
}
}
}
src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs
浏览文件 @
84d6a7a5
...
...
@@ -9,7 +9,7 @@
using
Roslyn.Test.Utilities
;
using
LSP
=
Microsoft
.
VisualStudio
.
LanguageServer
.
Protocol
;
namespace
Microsoft.CodeAnalysis.LanguageServer.UnitTests.
Definitions
namespace
Microsoft.CodeAnalysis.LanguageServer.UnitTests.
Rename
{
public
class
RenameTests
:
AbstractLanguageServerProtocolTests
{
...
...
@@ -36,6 +36,86 @@ void M2()
AssertJsonEquals
(
expectedEdits
,
results
.
DocumentChanges
.
First
().
Edits
);
}
[
WpfFact
]
public
async
Task
TestRename_WithLinkedFilesAsync
()
{
var
markup
=
@"class A
{
void {|caret:|}{|renamed:M|}()
{
}
void M2()
{
{|renamed:M|}()
}
}"
;
var
workspaceXml
=
$@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"" AssemblyName=""CSProj"" PreprocessorSymbols=""Proj1"">
<Document FilePath = ""C:\C.cs""><![CDATA[
{
markup
}
]]></Document>
</Project>
<Project Language = ""C#"" CommonReferences=""true"" PreprocessorSymbols=""Proj2"">
<Document IsLinkFile = ""true"" LinkAssemblyName=""CSProj"" LinkFilePath=""C:\C.cs""/>
</Project>
</Workspace>"
;
using
var
workspace
=
CreateXmlTestWorkspace
(
workspaceXml
,
out
var
locations
);
var
renameLocation
=
locations
[
"caret"
].
First
();
var
renameValue
=
"RENAME"
;
var
expectedEdits
=
locations
[
"renamed"
].
Select
(
location
=>
new
LSP
.
TextEdit
()
{
NewText
=
renameValue
,
Range
=
location
.
Range
});
var
results
=
await
RunRenameAsync
(
workspace
.
CurrentSolution
,
renameLocation
,
renameValue
);
AssertJsonEquals
(
expectedEdits
,
results
.
DocumentChanges
.
First
().
Edits
);
}
[
WpfFact
]
public
async
Task
TestRename_WithLinkedFilesAndPreprocessorAsync
()
{
var
markup
=
@"class A
{
void {|caret:|}{|renamed:M|}()
{
}
void M2()
{
{|renamed:M|}()
}
void M3()
{
#if Proj1
{|renamed:M|}()
#endif
}
void M4()
{
#if Proj2
{|renamed:M|}()
#endif
}
}"
;
var
workspaceXml
=
$@"<Workspace>
<Project Language=""C#"" CommonReferences=""true"" AssemblyName=""CSProj"" PreprocessorSymbols=""Proj1"">
<Document FilePath = ""C:\C.cs""><![CDATA[
{
markup
}
]]></Document>
</Project>
<Project Language = ""C#"" CommonReferences=""true"" PreprocessorSymbols=""Proj2"">
<Document IsLinkFile = ""true"" LinkAssemblyName=""CSProj"" LinkFilePath=""C:\C.cs""/>
</Project>
</Workspace>"
;
using
var
workspace
=
CreateXmlTestWorkspace
(
workspaceXml
,
out
var
locations
);
var
renameLocation
=
locations
[
"caret"
].
First
();
var
renameValue
=
"RENAME"
;
var
expectedEdits
=
locations
[
"renamed"
].
Select
(
location
=>
new
LSP
.
TextEdit
()
{
NewText
=
renameValue
,
Range
=
location
.
Range
});
var
results
=
await
RunRenameAsync
(
workspace
.
CurrentSolution
,
renameLocation
,
renameValue
);
AssertJsonEquals
(
expectedEdits
,
results
.
DocumentChanges
.
First
().
Edits
);
}
private
static
LSP
.
RenameParams
CreateRenameParams
(
LSP
.
Location
location
,
string
newName
)
=>
new
LSP
.
RenameParams
()
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录