diff --git a/BuildAndTest.proj b/BuildAndTest.proj
index a52bfaee728fa061ce4e3fa1285eb87a9189888d..b935914f61ba8447b7f4b6ede70f8f09184c0341 100644
--- a/BuildAndTest.proj
+++ b/BuildAndTest.proj
@@ -105,29 +105,9 @@
$(NuGetPackageRoot)\xunit.runner.console\$(xunitrunnerconsoleVersion)\tools $(RunTestArgs) @(TestAssemblies, ' ')
-
-
-
-
-
- $(OutputDirectory)\ProcessWatchdog\ProcessWatchdog.exe
- $(OutputDirectory)\ProcessWatchdogOutput
- C:\Sysinternals\Procdump.exe
-
-
- 300
-
-
- PowerShell.exe -ExecutionPolicy RemoteSigned -NoProfile .\build\scripts\Run-TestsWithProcessWatchdog.ps1 -ProcessWatchDogExe $(ProcessWatchDogExe) -ProcessWatchdogOutputDirectory $(ProcessWatchdogOutputDirectory) -ProcDumpExe $(ProcDumpExe) -CoreRunExe '$(CoreRunExe)' -CoreRunArgs '$(CoreRunArgs)' -RunTestsExe '$(RunTestsExe)' -RunTestsArgs '$(RunTestsArgs)' -BuildStartTime $(BuildStartTime) -BuildTimeLimit $(BuildTimeLimit) -BufferTime $(BufferTime)
-
+
-
+
diff --git a/Makefile b/Makefile
index 5e3621ac05780c63633975dd1865976b64e6b972..d64d2c97505605f678da73bc1aa17e31b19d8a04 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ RESTORE_SEMAPHORE_PATH = $(BINARIES_PATH)/restore.semaphore
BOOTSTRAP_PATH = $(BINARIES_PATH)/Bootstrap
BUILD_LOG_PATH =
HOME_DIR = $(shell cd ~ && pwd)
-DOTNET_VERSION = 1.0.0-preview2-002911
+DOTNET_VERSION = 1.0.0-preview2-003131
NUGET_VERSION = 3.5.0-beta2
NUGET_EXE = $(shell pwd)/nuget.exe
diff --git a/Restore.cmd b/Restore.cmd
index dba478a78eea3909934d133fdde8595d208ba8dd..422c17926f32f7772e9db2504a1ddb8a962089ed 100644
--- a/Restore.cmd
+++ b/Restore.cmd
@@ -1,4 +1,4 @@
-@echo off
+@if not defined EchoOn @echo off
@setlocal enabledelayedexpansion
set RoslynRoot=%~dp0
diff --git a/SetDevCommandPrompt.cmd b/SetDevCommandPrompt.cmd
index ad653379a61b07074ce5ad9bee0f69f319ab7079..b0056e43206aa3ed55406ace7d0ce58088fb430a 100644
--- a/SetDevCommandPrompt.cmd
+++ b/SetDevCommandPrompt.cmd
@@ -1,4 +1,4 @@
-@echo off
+@if not defined EchoOn @echo off
:: Prefer building with Dev15 and try the simple route first (we may be running from a DevCmdPrompt already)
set CommonToolsDir=%VS150COMNTOOLS%
diff --git a/build/MSBuildToolset/project.json b/build/MSBuildToolset/project.json
index aa89b1a4ce6af2f7cf7de909eeb83db2299d6497..f21f9380e0c1ccf094ff75913e440da0af7edf6c 100644
--- a/build/MSBuildToolset/project.json
+++ b/build/MSBuildToolset/project.json
@@ -20,7 +20,7 @@
"Newtonsoft.Json": "8.0.3"
},
"frameworks": {
- "NETCoreApp1.0": {
+ "NETCoreApp1.1": {
"imports": [
"portable-net452",
"dotnet"
@@ -29,6 +29,7 @@
},
"runtimes": {
"ubuntu.14.04-x64": {},
- "osx.10.11-x64": {}
+ "osx.10.11-x64": {},
+ "osx.10.12-x64": {}
}
}
diff --git a/build/Targets/Dependencies.props b/build/Targets/Dependencies.props
index 2a7271b22e7fdfa137d2ffeb0d4be683f1ca411d..46f8c229fb3f8963e6e4d5e078109a4718f43cd3 100644
--- a/build/Targets/Dependencies.props
+++ b/build/Targets/Dependencies.props
@@ -18,6 +18,7 @@
10.0.1
0.2.0-beta
0.2.0-beta
+ 0.2.1-beta
4.1.0
4.0.11
4.0.12
diff --git a/build/Targets/Settings.props b/build/Targets/Settings.props
index 168108294b0ac9cb2a9b0e56da4345c111cd939b..7e5e16b3a93509bbb1cf52e5a601a80534cbb66a 100644
--- a/build/Targets/Settings.props
+++ b/build/Targets/Settings.props
@@ -13,6 +13,8 @@
$(NuGetPackageRoot)
Microsoft.Net.Compilers
2.0.0-rc2-61102-09
+ RoslynTools.Microsoft.VSIXExpInstaller
+ 0.2.1-beta
1.2.0-beta2
$(NuGetPackageRoot)\Microsoft.Net.RoslynDiagnostics\$(RoslynDiagnosticsNugetPackageVersion)\build\Microsoft.Net.RoslynDiagnostics.props
$(NuGetPackageRoot)\Roslyn.Build.Util\0.9.4-portable\lib\dotnet\Roslyn.MSBuild.Util.dll
diff --git a/build/ToolsetPackages/project.json b/build/ToolsetPackages/project.json
index a1e0edf193350bbbdcd9033d87902a69392f7cfb..7b523e96e4d81c93a7a17fcc45129bd72d2c3318 100644
--- a/build/ToolsetPackages/project.json
+++ b/build/ToolsetPackages/project.json
@@ -14,7 +14,8 @@
"Roslyn.Build.Util": "0.9.4-portable",
"RoslynDependencies.OptimizationData": "2.0.0-rc-61101-16",
"RoslynTools.Microsoft.LocateVS": "0.2.0-beta",
- "RoslynTools.Microsoft.SignTool": "0.2.0-beta"
+ "RoslynTools.Microsoft.SignTool": "0.2.0-beta",
+ "RoslynTools.Microsoft.VSIXExpInstaller": "0.2.1-beta"
},
"frameworks": {
"net461": {}
diff --git a/cibuild.cmd b/cibuild.cmd
index c3e7b9a27127a238dd3b2ad6c2067b24989ec27e..70d174c7cb5428fd71e7cfe0637ba176326dee9c 100644
--- a/cibuild.cmd
+++ b/cibuild.cmd
@@ -75,11 +75,11 @@ copy "build\bootstrap\*" "%bindir%\Bootstrap" || goto :BuildFailed
REM Clean the previous build
msbuild %MSBuildAdditionalCommandLineArgs% /t:Clean build/Toolset/Toolset.csproj /p:Configuration=%BuildConfiguration% /fileloggerparameters:LogFile="%bindir%\BootstrapClean.log" || goto :BuildFailed
-call :TerminateBuildProcesses
+call :TerminateBuildProcesses || goto :BuildFailed
if defined TestDeterminism (
powershell -noprofile -executionPolicy RemoteSigned -file "%RoslynRoot%\build\scripts\test-determinism.ps1" "%bindir%\Bootstrap" || goto :BuildFailed
- call :TerminateBuildProcesses
+ call :TerminateBuildProcesses || goto :BuildFailed
exit /b 0
)
@@ -110,6 +110,8 @@ if defined TestPerfRun (
)
)
+ call :TerminateBuildProcesses || goto :BuildFailed
+
.\Binaries\%BuildConfiguration%\Exes\Perf.Runner\Roslyn.Test.Performance.Runner.exe --no-trace-upload !EXTRA_PERF_RUNNER_ARGS! || goto :BuildFailed
exit /b 0
)
@@ -117,7 +119,7 @@ if defined TestPerfRun (
msbuild %MSBuildAdditionalCommandLineArgs% /p:BootstrapBuildPath="%bindir%\Bootstrap" BuildAndTest.proj /p:Configuration=%BuildConfiguration% /p:Test64=%Test64% /p:TestVsi=%TestVsi% /p:RunProcessWatchdog=%RunProcessWatchdog% /p:BuildStartTime=%BuildStartTime% /p:"ProcDumpExe=%ProcDumpExe%" /p:BuildTimeLimit=%BuildTimeLimit% /p:PathMap="%RoslynRoot%=q:\roslyn" /p:Feature=pdb-path-determinism /fileloggerparameters:LogFile="%bindir%\Build.log";verbosity=diagnostic /p:DeployExtension=false || goto :BuildFailed
powershell -noprofile -executionPolicy RemoteSigned -file "%RoslynRoot%\build\scripts\check-msbuild.ps1" "%bindir%\Build.log" || goto :BuildFailed
-call :TerminateBuildProcesses
+call :TerminateBuildProcesses || goto :BuildFailed
REM Ensure caller sees successful exit.
exit /b 0
@@ -142,5 +144,12 @@ exit /b 1
@REM Kill any instances of msbuild.exe to ensure that we never reuse nodes (e.g. if a non-roslyn CI run
@REM left some floating around).
-taskkill /F /IM vbcscompiler.exe 2> nul
-taskkill /F /IM msbuild.exe 2> nul
+@REM An error-level of 1 means that the process was found, but could not be killed.
+echo Killing all build-related processes
+taskkill /F /IM msbuild.exe > nul
+if %ERRORLEVEL% == 1 exit /b 1
+
+taskkill /F /IM vbcscompiler.exe > nul
+if %ERRORLEVEL% == 1 exit /b 1
+
+exit /b 0
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 17e604ccec22198721bb18bede9340e22f8cb46e..0bebb66c717f2f833677ad3f6193985d60caf0e4 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -4411,7 +4411,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
generation but may be used by analyzers for producing
errors or warnings.
/embed Embed all source files in the PDB.
- /embed:<file list> Embed specfic files the PDB
+ /embed:<file list> Embed specific files in the PDB
- RESOURCES -
/win32res:<file> Specify a Win32 resource file (.res)
@@ -4975,4 +4975,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
A throw expression is not allowed in this context.
-
\ No newline at end of file
+
diff --git a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
index 62f69b414d546f6b1bc87e1645add7eaad5c1fb6..c990556b0e9e9dec91ebff1dca03da45d88d4132 100644
--- a/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
+++ b/src/EditorFeatures/CSharp/CSharpEditorFeatures.csproj
@@ -111,6 +111,7 @@
+
diff --git a/src/EditorFeatures/CSharp/FindReferences/CSharpFindReferencesService.cs b/src/EditorFeatures/CSharp/FindReferences/CSharpFindReferencesService.cs
index 0ac6e3c236ca4b754552a9c5fe33a7ceb73c4737..113664360a6925e2b83b52d4dbac08ac3c106604 100644
--- a/src/EditorFeatures/CSharp/FindReferences/CSharpFindReferencesService.cs
+++ b/src/EditorFeatures/CSharp/FindReferences/CSharpFindReferencesService.cs
@@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Composition;
+using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.FindReferences;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.FindReferences
@@ -14,22 +14,8 @@ internal class CSharpFindReferencesService : AbstractFindReferencesService
[ImportingConstructor]
public CSharpFindReferencesService(
[ImportMany] IEnumerable referencedSymbolsPresenters,
- [ImportMany] IEnumerable navigableItemsPresenters,
- [ImportMany] IEnumerable externalReferencesProviders)
- : base(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
- {
- }
- }
-
- [ExportLanguageService(typeof(IStreamingFindReferencesService), LanguageNames.CSharp), Shared]
- internal class CSharpStreamingFindReferencesService : AbstractFindReferencesService
- {
- [ImportingConstructor]
- public CSharpStreamingFindReferencesService(
- [ImportMany] IEnumerable referencedSymbolsPresenters,
- [ImportMany] IEnumerable navigableItemsPresenters,
- [ImportMany] IEnumerable externalReferencesProviders)
- : base(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
+ [ImportMany] IEnumerable navigableItemsPresenters)
+ : base(referencedSymbolsPresenters, navigableItemsPresenters)
{
}
}
diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5fb1df0e7dba84723a707c9d3127e2cf982d778c
--- /dev/null
+++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs
@@ -0,0 +1,13 @@
+// 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.Composition;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+namespace Microsoft.CodeAnalysis.Editor.CSharp.FindUsages
+{
+ [ExportLanguageService(typeof(IFindUsagesService), LanguageNames.CSharp), Shared]
+ internal class CSharpFindUsagesService : AbstractFindUsagesService
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
index 8dd47c6be1e497a37eff98214b521e0c33972c16..9366db7bd1422f995db428f66e53650f7ddddcd9 100644
--- a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
+++ b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Composition;
+using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
@@ -15,7 +15,8 @@ internal class CSharpGoToDefinitionService : AbstractGoToDefinitionService
[ImportingConstructor]
public CSharpGoToDefinitionService(
[ImportMany]IEnumerable> presenters,
- [ImportMany]IEnumerable> externalDefinitionProviders) : base(presenters, externalDefinitionProviders)
+ [ImportMany]IEnumerable> streamingPresenters)
+ : base(presenters, streamingPresenters)
{
}
@@ -24,4 +25,4 @@ protected override ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, C
return symbol;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/CSharp/GoToImplementation/CSharpGoToImplementationService.cs b/src/EditorFeatures/CSharp/GoToImplementation/CSharpGoToImplementationService.cs
index bc30c342b923a5f9ea48d29b48925931def8cd04..76cb1e814ebd37583f007a5d16995d194bf10b79 100644
--- a/src/EditorFeatures/CSharp/GoToImplementation/CSharpGoToImplementationService.cs
+++ b/src/EditorFeatures/CSharp/GoToImplementation/CSharpGoToImplementationService.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Composition;
+using Microsoft.CodeAnalysis.Editor.GoToImplementation;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToImplementation
@@ -14,9 +14,9 @@ internal sealed class CSharpGoToImplementationService : AbstractGoToImplementati
{
[ImportingConstructor]
public CSharpGoToImplementationService(
- [ImportMany]IEnumerable> presenters,
- [ImportMany]IEnumerable> externalDefinitionProviders) : base(presenters, externalDefinitionProviders)
+ [ImportMany]IEnumerable> presenters)
+ : base(presenters)
{
}
}
-}
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
index a3679231f33058ae9ec0b7b79d049dceb5befe2b..ffa11ba4f059bf2985c2b1b4f5f57c8d6acf5272 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
@@ -1,11 +1,14 @@
// 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.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.ExtractMethod;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Microsoft.CodeAnalysis.Options;
using Roslyn.Test.Utilities;
using Xunit;
@@ -14,9 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.Extrac
public class ExtractMethodTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace)
- {
- return new ExtractMethodCodeRefactoringProvider();
- }
+ => new ExtractMethodCodeRefactoringProvider();
[WorkItem(540799, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540799")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
@@ -1213,5 +1214,101 @@ private static void NewMethod(int v, CancellationToken ct)
}
}");
}
+
+ [WorkItem(15219, "https://github.com/dotnet/roslyn/issues/15219")]
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
+ public async Task TestUseVar1()
+ {
+ await TestAsync(
+@"using System;
+
+class C
+{
+ void Foo(int i)
+ {
+ [|var v = (string)null;
+
+ switch (i)
+ {
+ case 0: v = ""0""; break;
+ case 1: v = ""1""; break;
+ }|]
+
+ Console.WriteLine(v);
+ }
+}",
+@"using System;
+
+class C
+{
+ void Foo(int i)
+ {
+ var v = {|Rename:NewMethod|}(i);
+
+ Console.WriteLine(v);
+ }
+
+ private static string NewMethod(int i)
+ {
+ var v = (string)null;
+
+ switch (i)
+ {
+ case 0: v = ""0""; break;
+ case 1: v = ""1""; break;
+ }
+
+ return v;
+ }
+}", options: Option(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithSuggestionEnforcement));
+ }
+
+ [WorkItem(15219, "https://github.com/dotnet/roslyn/issues/15219")]
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
+ public async Task TestUseVar2()
+ {
+ await TestAsync(
+@"using System;
+
+class C
+{
+ void Foo(int i)
+ {
+ [|var v = (string)null;
+
+ switch (i)
+ {
+ case 0: v = ""0""; break;
+ case 1: v = ""1""; break;
+ }|]
+
+ Console.WriteLine(v);
+ }
+}",
+@"using System;
+
+class C
+{
+ void Foo(int i)
+ {
+ string v = {|Rename:NewMethod|}(i);
+
+ Console.WriteLine(v);
+ }
+
+ private static string NewMethod(int i)
+ {
+ var v = (string)null;
+
+ switch (i)
+ {
+ case 0: v = ""0""; break;
+ case 1: v = ""1""; break;
+ }
+
+ return v;
+ }
+}", options: Option(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithSuggestionEnforcement));
+ }
}
}
\ No newline at end of file
diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionServiceTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionServiceTests.cs
index 3672915a18c87174834d25991ef261bb8603522c..4a973ee88941b97404deae3e7d2a55b1e3161367 100644
--- a/src/EditorFeatures/CSharpTest/Completion/CompletionServiceTests.cs
+++ b/src/EditorFeatures/CSharpTest/Completion/CompletionServiceTests.cs
@@ -14,15 +14,7 @@ public class CompletionServiceTests
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public void AcquireCompletionService()
{
- var hostServices = MefHostServices.Create(
- MefHostServices.DefaultAssemblies.Concat(
- new[]
- {
- typeof(CompletionService).Assembly,
- typeof(CSharpCompletionService).Assembly
- }));
-
- var workspace = new AdhocWorkspace(hostServices);
+ var workspace = new AdhocWorkspace();
var document = workspace
.AddProject("TestProject", LanguageNames.CSharp)
diff --git a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs
index dcd7891ba34ecd68c46b2e4648c646a2c5c2fda0..7e81e584ba3eb5cbc9261a58c1c6a84588005611 100644
--- a/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs
+++ b/src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs
@@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ExtractMethod;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
@@ -82,7 +83,9 @@ protected async Task NotSupported_ExtractMethodAsync(string codeWithMarker)
var testDocument = workspace.Documents.Single();
var subjectBuffer = testDocument.TextBuffer;
- var tree = await ExtractMethodAsync(workspace, testDocument, allowMovingDeclaration: allowMovingDeclaration, dontPutOutOrRefOnStruct: dontPutOutOrRefOnStruct);
+ var tree = await ExtractMethodAsync(
+ workspace, testDocument, allowMovingDeclaration: allowMovingDeclaration,
+ dontPutOutOrRefOnStruct: dontPutOutOrRefOnStruct);
using (var edit = subjectBuffer.CreateEdit())
{
diff --git a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs
index adc65ad09faa311bbb7c6bc02f54395c2d0987bd..4e077298cc65631cb60149110e01845f1a549887 100644
--- a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs
+++ b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs
@@ -471,7 +471,7 @@ void M()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
- public async Task TestFixAllInDocument()
+ public async Task TestFixAllInDocument1()
{
await TestAsync(
@"using System.Collections.Generic;
@@ -510,6 +510,78 @@ void M()
}");
}
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
+ public async Task TestFixAllInDocument2()
+ {
+ await TestAsync(
+@"using System.Collections.Generic;
+
+class C
+{
+ void M()
+ {
+ var list1 = {|FixAllInDocument:new|} List(() => {
+ var list2 = new List();
+ list2.Add(2);
+ });
+ list1.Add(1);
+ }
+}",
+@"using System.Collections.Generic;
+
+class C
+{
+ void M()
+ {
+ var list1 = new List(() => {
+ var list2 = new List
+ {
+ 2
+ };
+ })
+ {
+ 1
+ };
+ }
+}");
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
+ public async Task TestFixAllInDocument3()
+ {
+ await TestAsync(
+@"using System.Collections.Generic;
+
+class C
+{
+ void M()
+ {
+ var list1 = {|FixAllInDocument:new|} List();
+ list1.Add(() => {
+ var list2 = new List();
+ list2.Add(2);
+ });
+ }
+}",
+@"using System.Collections.Generic;
+
+class C
+{
+ void M()
+ {
+ var list1 = new List
+ {
+ () => {
+ var list2 = new List
+ {
+ 2
+ };
+ }
+ };
+ }
+}");
+ }
+
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestTrivia1()
{
diff --git a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs
index 45da9296ece09f431ba1c0a63c4f0f3b921864fd..e0ba7d7cfa0650fbdfe8e5093ec4989062c0e611 100644
--- a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs
+++ b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs
@@ -305,7 +305,83 @@ void M()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
- public async Task TestFixAllInDocument()
+ public async Task TestFixAllInDocument1()
+ {
+ await TestAsync(
+@"class C
+{
+ int i;
+ int j;
+
+ void M()
+ {
+ var v = {|FixAllInDocument:new|} C(() => {
+ var v2 = new C();
+ v2.i = 1;
+ });
+ v.j = 2;
+ }
+}",
+@"class C
+{
+ int i;
+ int j;
+
+ void M()
+ {
+ var v = new C(() => {
+ var v2 = new C()
+ {
+ i = 1
+ };
+ })
+ {
+ j = 2
+ };
+ }
+}");
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
+ public async Task TestFixAllInDocument2()
+ {
+ await TestAsync(
+@"class C
+{
+ int i;
+ int j;
+
+ void M()
+ {
+ var v = {|FixAllInDocument:new|} C();
+ v.j = () => {
+ var v2 = new C();
+ v2.i = 1;
+ };
+ }
+}",
+@"class C
+{
+ int i;
+ int j;
+
+ void M()
+ {
+ var v = new C()
+ {
+ j = () => {
+ var v2 = new C()
+ {
+ i = 1
+ };
+ }
+ };
+ }
+}");
+ }
+
+ [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
+ public async Task TestFixAllInDocument3()
{
await TestAsync(
@"class C
diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs
index 8c112bb88e47fa71315c89ff7df6842c077dfa8e..5a321ba27fb903e43e39b7e5b1362abb2ec5014f 100644
--- a/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs
+++ b/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs
@@ -128,6 +128,120 @@ public async Task TestNotAfterPartialInClass()
class Foo
{
partial $$
+}");
+ }
+
+ [Fact]
+ [WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ public async Task TestLocalFunction()
+ {
+ await VerifyKeywordAsync(@"
+class Foo
+{
+ public void M()
+ {
+ $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction2()
+ {
+ await VerifyKeywordAsync(@"
+class Foo
+{
+ public void M()
+ {
+ unsafe $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction3()
+ {
+ await VerifyKeywordAsync(@"
+class Foo
+{
+ public void M()
+ {
+ unsafe $$ void L() { }
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction4()
+ {
+ await VerifyKeywordAsync(@"
+class Foo
+{
+ public void M()
+ {
+ $$ void L() { }
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction5()
+ {
+ await VerifyKeywordAsync(@"
+class Foo
+{
+ public void M(Action a)
+ {
+ M(async () =>
+ {
+ $$
+ });
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction6()
+ {
+ await VerifyAbsenceAsync(@"
+class Foo
+{
+ public void M()
+ {
+ int $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction7()
+ {
+ await VerifyAbsenceAsync(@"
+class Foo
+{
+ public void M()
+ {
+ static $$
+ }
}");
}
}
diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
index 0f96afeb59ff846d2ef1402d29124e12832d4392..e521a19b61862e5e5bfba557d84859efba54f520 100644
--- a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
+++ b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs
@@ -83,13 +83,6 @@ public async Task TestNotInCastType2()
@"var str = (($$)items) as string;"));
}
- [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
- public async Task TestNotInEmptyStatement()
- {
- await VerifyAbsenceAsync(AddInsideMethod(
-@"$$"));
- }
-
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInTypeOf()
{
@@ -642,5 +635,123 @@ public async Task TestNotAfterAsyncAsType()
{
await VerifyAbsenceAsync(@"class c { async async $$ }");
}
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction()
+ {
+ await VerifyKeywordAsync(@"
+class C
+{
+ void M()
+ {
+ $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction2()
+ {
+ await VerifyKeywordAsync(@"
+class C
+{
+ void M()
+ {
+ async $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction3()
+ {
+ await VerifyAbsenceAsync(@"
+class C
+{
+ void M()
+ {
+ async async $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction4()
+ {
+ await VerifyAbsenceAsync(@"
+class C
+{
+ void M()
+ {
+ var async $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction5()
+ {
+ await VerifyAbsenceAsync(@"
+using System;
+class C
+{
+ void M(Action a)
+ {
+ M(async $$ () =>
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction6()
+ {
+ await VerifyKeywordAsync(@"
+class C
+{
+ void M()
+ {
+ unsafe async $$
+ }
+}");
+ }
+
+ [Fact]
+ [WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
+ [Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
+ [Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
+ public async Task TestLocalFunction7()
+ {
+ await VerifyKeywordAsync(@"
+using System;
+class C
+{
+ void M(Action a)
+ {
+ M(async () =>
+ {
+ async $$
+ })
+ }
+}");
+ }
}
}
diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj
index 9433e6f4c89f6c5ba122575309bf636aba120fa5..207765788c176339ecf7fbc4c3676dde8bdddc7b 100644
--- a/src/EditorFeatures/Core/EditorFeatures.csproj
+++ b/src/EditorFeatures/Core/EditorFeatures.csproj
@@ -108,6 +108,12 @@
+
+
+
+
+
+
@@ -254,12 +260,9 @@
-
-
-
-
-
-
+
+
+
@@ -280,7 +283,7 @@
-
+
@@ -377,9 +380,9 @@
-
-
-
+
+
+
@@ -390,10 +393,10 @@
-
-
-
-
+
+
+
+
@@ -782,4 +785,4 @@
-
+
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/FindReferences/AbstractFindReferencesService.cs b/src/EditorFeatures/Core/FindReferences/AbstractFindReferencesService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..38d6b6d8400d0883f1fb72be3e8b024f862c411f
--- /dev/null
+++ b/src/EditorFeatures/Core/FindReferences/AbstractFindReferencesService.cs
@@ -0,0 +1,104 @@
+// 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.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Navigation;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.Editor.FindReferences
+{
+ internal abstract partial class AbstractFindReferencesService :
+ ForegroundThreadAffinitizedObject, IFindReferencesService
+ {
+ private readonly IEnumerable _referenceSymbolPresenters;
+ private readonly IEnumerable _navigableItemPresenters;
+
+ protected AbstractFindReferencesService(
+ IEnumerable referenceSymbolPresenters,
+ IEnumerable navigableItemPresenters)
+ {
+ _referenceSymbolPresenters = referenceSymbolPresenters;
+ _navigableItemPresenters = navigableItemPresenters;
+ }
+
+ private async Task, Solution>> FindReferencedSymbolsAsync(
+ Document document, int position, IWaitContext waitContext)
+ {
+ var cancellationToken = waitContext.CancellationToken;
+
+ var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
+ document, position, cancellationToken).ConfigureAwait(false);
+ if (symbolAndProject == null)
+ {
+ return null;
+ }
+
+ var symbol = symbolAndProject?.symbol;
+ var project = symbolAndProject?.project;
+
+ var displayName = GetDisplayName(symbol);
+
+ waitContext.Message = string.Format(
+ EditorFeaturesResources.Finding_references_of_0, displayName);
+
+ var result = await SymbolFinder.FindReferencesAsync(symbol, project.Solution, cancellationToken).ConfigureAwait(false);
+
+ return Tuple.Create(result, project.Solution);
+ }
+
+ public static string GetDisplayName(ISymbol symbol)
+ {
+ return symbol.IsConstructor() ? symbol.ContainingType.Name : symbol.Name;
+ }
+
+ public bool TryFindReferences(Document document, int position, IWaitContext waitContext)
+ {
+ var cancellationToken = waitContext.CancellationToken;
+
+ var result = this.FindReferencedSymbolsAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
+ return TryDisplayReferences(result);
+ }
+
+ private bool TryDisplayReferences(IEnumerable result)
+ {
+ if (result != null && result.Any())
+ {
+ var title = result.First().DisplayTaggedParts.JoinText();
+ foreach (var presenter in _navigableItemPresenters)
+ {
+ presenter.DisplayResult(title, result);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool TryDisplayReferences(Tuple, Solution> result)
+ {
+ if (result != null && result.Item1 != null)
+ {
+ var solution = result.Item2;
+ var factory = solution.Workspace.Services.GetService();
+ var definitionsAndReferences = factory.CreateDefinitionsAndReferences(
+ solution, result.Item1);
+
+ foreach (var presenter in _referenceSymbolPresenters)
+ {
+ presenter.DisplayResult(definitionsAndReferences);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesCommandHandler.cs b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs
similarity index 85%
rename from src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesCommandHandler.cs
rename to src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs
index 553f4f93d97a5507084725c03da1995bc6246b9a..917961145c52824ea323a34a21dc4f578c174dd8 100644
--- a/src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesCommandHandler.cs
+++ b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs
@@ -5,24 +5,26 @@
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.Editor.Commands;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.ErrorReporting;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
+namespace Microsoft.CodeAnalysis.Editor.FindReferences
{
[ExportCommandHandler(PredefinedCommandHandlerNames.FindReferences, ContentTypeNames.RoslynContentType)]
internal class FindReferencesCommandHandler : ICommandHandler
{
private readonly IEnumerable _synchronousPresenters;
- private readonly IEnumerable> _streamingPresenters;
+ private readonly IEnumerable> _streamingPresenters;
private readonly IWaitIndicator _waitIndicator;
private readonly IAsynchronousOperationListener _asyncListener;
@@ -31,7 +33,7 @@ internal class FindReferencesCommandHandler : ICommandHandler synchronousPresenters,
- [ImportMany] IEnumerable> streamingPresenters,
+ [ImportMany] IEnumerable> streamingPresenters,
[ImportMany] IEnumerable> asyncListeners)
{
Contract.ThrowIfNull(synchronousPresenters);
@@ -72,8 +74,8 @@ public void ExecuteCommand(FindReferencesCommandArgs args, Action nextHandler)
private bool TryExecuteCommand(int caretPosition, Document document)
{
- var streamingService = document.Project.LanguageServices.GetService();
- var synchronousService = document.Project.LanguageServices.GetService();
+ var streamingService = document.GetLanguageService();
+ var synchronousService = document.GetLanguageService();
var streamingPresenter = GetStreamingPresenter();
@@ -83,7 +85,7 @@ private bool TryExecuteCommand(int caretPosition, Document document)
var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingFindReferences, document.Project.Language);
if (streamingEnabled && streamingService != null && streamingPresenter != null)
{
- StreamingFindReferences(document, streamingService, streamingPresenter, caretPosition);
+ StreamingFindReferences(document, caretPosition, streamingService, streamingPresenter);
return true;
}
@@ -100,7 +102,7 @@ private bool TryExecuteCommand(int caretPosition, Document document)
return false;
}
- private IStreamingFindReferencesPresenter GetStreamingPresenter()
+ private IStreamingFindUsagesPresenter GetStreamingPresenter()
{
try
{
@@ -113,8 +115,9 @@ private IStreamingFindReferencesPresenter GetStreamingPresenter()
}
private async void StreamingFindReferences(
- Document document, IStreamingFindReferencesService service,
- IStreamingFindReferencesPresenter presenter, int caretPosition)
+ Document document, int caretPosition,
+ IFindUsagesService findUsagesService,
+ IStreamingFindUsagesPresenter presenter)
{
try
{
@@ -122,8 +125,8 @@ private IStreamingFindReferencesPresenter GetStreamingPresenter()
{
// Let the presented know we're starging a search. It will give us back
// the context object that the FAR service will push results into.
- var context = presenter.StartSearch();
- await service.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
+ var context = presenter.StartSearch(EditorFeaturesResources.Find_References);
+ await findUsagesService.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
// Note: we don't need to put this in a finally. The only time we might not hit
// this is if cancellation or another error gets thrown. In the former case,
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesService.cs b/src/EditorFeatures/Core/FindReferences/IFindReferencesService.cs
similarity index 64%
rename from src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesService.cs
rename to src/EditorFeatures/Core/FindReferences/IFindReferencesService.cs
index 6d09c840a13207e46ea0c01f9f5c243276eeeff6..9de01830b82b1e3c00a9ab9bc014b4de6d0d8042 100644
--- a/src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesService.cs
+++ b/src/EditorFeatures/Core/FindReferences/IFindReferencesService.cs
@@ -1,6 +1,5 @@
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Host;
@@ -15,13 +14,4 @@ internal interface IFindReferencesService : ILanguageService
/// True if finding references of the symbol at the provided position succeeds. False, otherwise.
bool TryFindReferences(Document document, int position, IWaitContext waitContext);
}
-
- internal interface IStreamingFindReferencesService : ILanguageService
- {
- ///
- /// Finds the references for the symbol at the specific position in the document,
- /// pushing the results into the context instance.
- ///
- Task FindReferencesAsync(Document document, int position, FindReferencesContext context);
- }
}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.ProgressAdapter.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs
similarity index 91%
rename from src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.ProgressAdapter.cs
rename to src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs
index 9b99c044052530336fec5ca160c7660272ad588d..4ba183f5d6c5f494c969c8308300005d4ee62835 100644
--- a/src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.ProgressAdapter.cs
+++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs
@@ -4,22 +4,22 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
- internal abstract partial class AbstractFindReferencesService
+ internal abstract partial class AbstractFindUsagesService
{
///
- /// Forwards IFindReferencesProgress calls to a FindRefrencesContext instance.
+ /// Forwards IFindReferencesProgress calls to an IFindUsagesContext instance.
///
private class ProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFindReferencesProgress
{
private readonly Solution _solution;
- private readonly FindReferencesContext _context;
+ private readonly IFindUsagesContext _context;
///
/// We will hear about definition symbols many times while performing FAR. We'll
@@ -36,7 +36,7 @@ private class ProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFin
private readonly Func _definitionFactory;
- public ProgressAdapter(Solution solution, FindReferencesContext context)
+ public ProgressAdapter(Solution solution, IFindUsagesContext context)
{
_solution = solution;
_context = context;
diff --git a/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e15763a8319014070a3c5c5acfdebe0558eca47d
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/AbstractFindUsagesService.cs
@@ -0,0 +1,100 @@
+// 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.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.FindReferences;
+using Microsoft.CodeAnalysis.Editor.GoToImplementation;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.FindUsages;
+
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
+{
+ internal abstract partial class AbstractFindUsagesService : IFindUsagesService
+ {
+ public async Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context)
+ {
+ var tuple = await FindUsagesHelpers.FindImplementationsAsync(
+ document, position, context.CancellationToken).ConfigureAwait(false);
+ if (tuple == null)
+ {
+ context.ReportMessage(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
+ return;
+ }
+
+ var message = tuple.Value.message;
+
+ if (message != null)
+ {
+ context.ReportMessage(message);
+ return;
+ }
+
+ var project = tuple.Value.project;
+
+ foreach (var implementation in tuple.Value.implementations)
+ {
+ var definitionItem = implementation.ToDefinitionItem(project.Solution);
+ await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
+ }
+ }
+
+ public async Task FindReferencesAsync(
+ Document document, int position, IFindUsagesContext context)
+ {
+ // NOTE: All ConFigureAwaits in this method need to pass 'true' so that
+ // we return to the caller's context. that's so the call to
+ // CallThirdPartyExtensionsAsync will happen on the UI thread. We need
+ // this to maintain the threading guarantee we had around that method
+ // from pre-Roslyn days.
+ var findReferencesProgress = await FindReferencesWorkerAsync(
+ document, position, context).ConfigureAwait(true);
+ if (findReferencesProgress == null)
+ {
+ return;
+ }
+
+ // After the FAR engine is done call into any third party extensions to see
+ // if they want to add results.
+ await findReferencesProgress.CallThirdPartyExtensionsAsync().ConfigureAwait(true);
+ }
+
+ private async Task FindReferencesWorkerAsync(
+ Document document, int position, IFindUsagesContext context)
+ {
+ var cancellationToken = context.CancellationToken;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Find the symbol we want to search and the solution we want to search in.
+ var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
+ document, position, cancellationToken).ConfigureAwait(false);
+ if (symbolAndProject == null)
+ {
+ return null;
+ }
+
+ var symbol = symbolAndProject?.symbol;
+ var project = symbolAndProject?.project;
+
+ var displayName = AbstractFindReferencesService.GetDisplayName(symbol);
+ context.SetSearchLabel(displayName);
+
+ var progressAdapter = new ProgressAdapter(project.Solution, context);
+
+ // Now call into the underlying FAR engine to find reference. The FAR
+ // engine will push results into the 'progress' instance passed into it.
+ // We'll take those results, massage them, and forward them along to the
+ // FindReferencesContext instance we were given.
+ await SymbolFinder.FindReferencesAsync(
+ SymbolAndProjectId.Create(symbol, project.Id),
+ project.Solution,
+ progressAdapter,
+ documents: null,
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return progressAdapter;
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesContext.cs b/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs
similarity index 77%
rename from src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesContext.cs
rename to src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs
index 5c7f8d2f7799defbd0e2b7f0ee6f0a5ac289d8ed..e64900c8c165d4186540159577427a81766cefd3 100644
--- a/src/EditorFeatures/Core/Implementation/FindReferences/FindReferencesContext.cs
+++ b/src/EditorFeatures/Core/FindUsages/FindUsagesContext.cs
@@ -1,17 +1,21 @@
// 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.Threading;
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.FindReferences;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.Editor
+namespace Microsoft.CodeAnalysis.FindUsages
{
- internal abstract class FindReferencesContext
+ internal abstract class FindUsagesContext : IFindUsagesContext
{
public virtual CancellationToken CancellationToken { get; }
- protected FindReferencesContext()
+ protected FindUsagesContext()
+ {
+ }
+
+ public virtual void ReportMessage(string message)
{
}
diff --git a/src/EditorFeatures/Core/FindUsages/FindUsagesHelpers.cs b/src/EditorFeatures/Core/FindUsages/FindUsagesHelpers.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e28f739a0dbe58a2619d68b4e43751e4674d329e
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/FindUsagesHelpers.cs
@@ -0,0 +1,132 @@
+// 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.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.SymbolMapping;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
+{
+ internal static class FindUsagesHelpers
+ {
+ ///
+ /// Common helper for both the synchronous and streaming versions of FAR.
+ /// It returns the symbol we want to search for and the solution we should
+ /// be searching.
+ ///
+ /// Note that the returned may absolutely *not* be
+ /// the same as document.Project.Solution
. This is because
+ /// there may be symbol mapping involved (for example in Metadata-As-Source
+ /// scenarios).
+ ///
+ public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
+ Document document, int position, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
+ if (symbol == null)
+ {
+ return null;
+ }
+
+ // If this document is not in the primary workspace, we may want to search for results
+ // in a solution different from the one we started in. Use the starting workspace's
+ // ISymbolMappingService to get a context for searching in the proper solution.
+ var mappingService = document.Project.Solution.Workspace.Services.GetService();
+
+ var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
+ if (mapping == null)
+ {
+ return null;
+ }
+
+ return (mapping.Symbol, mapping.Project);
+ }
+
+ public static async Task<(ISymbol symbol, Project project, ImmutableArray implementations, string message)?> FindImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
+ {
+ var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(
+ document, position, cancellationToken).ConfigureAwait(false);
+ if (symbolAndProject == null)
+ {
+ return null;
+ }
+
+ return await FindImplementationsAsync(
+ symbolAndProject?.symbol, symbolAndProject?.project, cancellationToken).ConfigureAwait(false);
+ }
+
+ private static async Task<(ISymbol symbol, Project project, ImmutableArray implementations, string message)?> FindImplementationsAsync(
+ ISymbol symbol, Project project, CancellationToken cancellationToken)
+ {
+ var implementations = await FindImplementationsWorkerAsync(
+ symbol, project, cancellationToken).ConfigureAwait(false);
+
+ var filteredSymbols = implementations.WhereAsArray(
+ s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource));
+
+ return filteredSymbols.Length == 0
+ ? (symbol, project, filteredSymbols, EditorFeaturesResources.The_symbol_has_no_implementations)
+ : (symbol, project, filteredSymbols, null);
+ }
+
+ private static async Task> FindImplementationsWorkerAsync(
+ ISymbol symbol, Project project, CancellationToken cancellationToken)
+ {
+ var solution = project.Solution;
+ if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
+ {
+ var implementations = await SymbolFinder.FindImplementationsAsync(
+ symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ // It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method
+ // in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
+ // is to just use a HashSet.
+ var implementationsAndOverrides = new HashSet();
+
+ foreach (var implementation in implementations)
+ {
+ implementationsAndOverrides.Add(implementation);
+
+ // FindImplementationsAsync will only return the base virtual/abstract method, not that method and the overrides
+ // of the method. We should also include those.
+ if (implementation.IsOverridable())
+ {
+ var overrides = await SymbolFinder.FindOverridesAsync(
+ implementation, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
+ implementationsAndOverrides.AddRange(overrides);
+ }
+ }
+
+ return implementationsAndOverrides.ToImmutableArray();
+ }
+ else if ((symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
+ {
+ var derivedClasses = await SymbolFinder.FindDerivedClassesAsync(
+ (INamedTypeSymbol)symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
+ var implementations = derivedClasses.Concat(symbol);
+
+ return implementations.ToImmutableArray();
+ }
+ else if (symbol.IsOverridable())
+ {
+ var overrides = await SymbolFinder.FindOverridesAsync(
+ symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
+ var implementations = overrides.Concat(symbol);
+
+ return implementations.ToImmutableArray();
+ }
+ else
+ {
+ // This is something boring like a regular method or type, so we'll just go there directly
+ return ImmutableArray.Create(symbol);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs b/src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..35f34ab9c11724e3802d88992fe1c1f4f67db0a4
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/IFindUsagesContext.cs
@@ -0,0 +1,27 @@
+// 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.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.CodeAnalysis.FindUsages
+{
+ internal interface IFindUsagesContext
+ {
+ CancellationToken CancellationToken { get; }
+
+ ///
+ /// Report a message to be displayed to the user.
+ ///
+ void ReportMessage(string message);
+
+ ///
+ /// Set the title of the window that results are displayed in.
+ ///
+ void SetSearchLabel(string displayName);
+
+ Task OnDefinitionFoundAsync(DefinitionItem definition);
+ Task OnReferenceFoundAsync(SourceReferenceItem reference);
+
+ Task ReportProgressAsync(int current, int maximum);
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/FindUsages/IFindUsagesService.cs b/src/EditorFeatures/Core/FindUsages/IFindUsagesService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b0dda6dc4af9d00b179de31efcc5c756582cb9bd
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/IFindUsagesService.cs
@@ -0,0 +1,23 @@
+// 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.Threading.Tasks;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Host;
+
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
+{
+ internal interface IFindUsagesService : ILanguageService
+ {
+ ///
+ /// Finds the references for the symbol at the specific position in the document,
+ /// pushing the results into the context instance.
+ ///
+ Task FindReferencesAsync(Document document, int position, IFindUsagesContext context);
+
+ ///
+ /// Finds the implementationss for the symbol at the specific position in the document,
+ /// pushing the results into the context instance.
+ ///
+ Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context);
+ }
+}
diff --git a/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs b/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..23a183825879bfe0f3a96c97462a327590439d46
--- /dev/null
+++ b/src/EditorFeatures/Core/FindUsages/SimpleFindUsagesContext.cs
@@ -0,0 +1,72 @@
+// 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.Collections.Immutable;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.FindUsages;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.Editor.FindUsages
+{
+ ///
+ /// Simple implementation of a that just aggregates the results
+ /// for consumers that just want the data once it is finally computed.
+ ///
+ internal class SimpleFindUsagesContext : FindUsagesContext
+ {
+ private readonly object _gate = new object();
+ private readonly ImmutableArray.Builder _definitionItems =
+ ImmutableArray.CreateBuilder();
+
+ private readonly ImmutableArray.Builder _referenceItems =
+ ImmutableArray.CreateBuilder();
+
+ public override CancellationToken CancellationToken { get; }
+
+ public SimpleFindUsagesContext(CancellationToken cancellationToken)
+ {
+ CancellationToken = cancellationToken;
+ }
+
+ public string Message { get; private set; }
+
+ public override void ReportMessage(string message)
+ => Message = message;
+
+ public ImmutableArray GetDefinitions()
+ {
+ lock (_gate)
+ {
+ return _definitionItems.ToImmutable();
+ }
+ }
+
+ public ImmutableArray GetReferences()
+ {
+ lock (_gate)
+ {
+ return _referenceItems.ToImmutable();
+ }
+ }
+
+ public override Task OnDefinitionFoundAsync(DefinitionItem definition)
+ {
+ lock (_gate)
+ {
+ _definitionItems.Add(definition);
+ }
+
+ return SpecializedTasks.EmptyTask;
+ }
+
+ public override Task OnReferenceFoundAsync(SourceReferenceItem reference)
+ {
+ lock (_gate)
+ {
+ _referenceItems.Add(reference);
+ }
+
+ return SpecializedTasks.EmptyTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/GoToDefinition/AbstractGoToDefinitionService.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs
similarity index 76%
rename from src/EditorFeatures/Core/Implementation/GoToDefinition/AbstractGoToDefinitionService.cs
rename to src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs
index 888a9e3ca85f1e97d11b0b34a9fbc12e6dfe31c1..06aae0270d341eeb7a98f87ef3eb0be3111cf91d 100644
--- a/src/EditorFeatures/Core/Implementation/GoToDefinition/AbstractGoToDefinitionService.cs
+++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs
@@ -12,19 +12,21 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
+namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
{
private readonly IEnumerable> _presenters;
- private readonly IEnumerable> _externalDefinitionProviders;
+ private readonly IEnumerable> _streamingPresenters;
protected abstract ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation);
- protected AbstractGoToDefinitionService(IEnumerable> presenters, IEnumerable> externalDefinitionProviders)
+ protected AbstractGoToDefinitionService(
+ IEnumerable> presenters,
+ IEnumerable> streamingPresenters)
{
_presenters = presenters;
- _externalDefinitionProviders = externalDefinitionProviders;
+ _streamingPresenters = streamingPresenters;
}
private async Task FindSymbolAsync(Document document, int position, CancellationToken cancellationToken)
@@ -59,11 +61,6 @@ public async Task> FindDefinitionsAsync(Document doc
var items = symbol != null
? NavigableItemFactory.GetItemsFromPreferredSourceLocations(document.Project.Solution, symbol, displayTaggedParts: null)
: null;
- if (items == null || items.IsEmpty())
- {
- // Fallback to asking the navigation definition providers for navigable definition locations.
- items = await GoToDefinitionHelpers.FindExternalDefinitionsAsync(document, position, _externalDefinitionProviders, cancellationToken).ConfigureAwait(false);
- }
// realize the list here so that the consumer await'ing the result doesn't lazily cause
// them to be created on an inappropriate thread.
@@ -74,21 +71,20 @@ public bool TryGoToDefinition(Document document, int position, CancellationToken
{
// First try to compute the referenced symbol and attempt to go to definition for the symbol.
var symbol = FindSymbolAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
- if (symbol != null)
+ if (symbol == null)
{
- var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);
-
- return GoToDefinitionHelpers.TryGoToDefinition(symbol,
- document.Project,
- _externalDefinitionProviders,
- _presenters,
- thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
- throwOnHiddenDefinition: true,
- cancellationToken: cancellationToken);
+ return false;
}
- // Otherwise, fallback to the external navigation definition providers.
- return GoToDefinitionHelpers.TryExternalGoToDefinition(document, position, _externalDefinitionProviders, _presenters, cancellationToken);
+ var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);
+
+ return GoToDefinitionHelpers.TryGoToDefinition(symbol,
+ document.Project,
+ _presenters,
+ _streamingPresenters,
+ thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
+ throwOnHiddenDefinition: true,
+ cancellationToken: cancellationToken);
}
private static bool IsThirdPartyNavigationAllowed(ISymbol symbolToNavigateTo, int caretPosition, Document document, CancellationToken cancellationToken)
diff --git a/src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionCommandHandler.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs
similarity index 98%
rename from src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionCommandHandler.cs
rename to src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs
index 9e4f668006d9a32a124be4f61d8b7fcc58ecc085..43ca3a34f9b61f1f384db67425e8d448a407ab69 100644
--- a/src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionCommandHandler.cs
+++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs
@@ -9,7 +9,7 @@
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
-namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
+namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToDefinition,
ContentTypeNames.RoslynContentType)]
diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs
new file mode 100644
index 0000000000000000000000000000000000000000..feb070177c1d61085a777c091689ef6148dc5410
--- /dev/null
+++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs
@@ -0,0 +1,199 @@
+// 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.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Navigation;
+using Microsoft.CodeAnalysis.Options;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis.FindUsages;
+
+namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
+{
+ internal static class GoToDefinitionHelpers
+ {
+ public static bool TryGoToDefinition(
+ ISymbol symbol,
+ Project project,
+ IEnumerable> presenters,
+ IEnumerable> streamingPresenters,
+ CancellationToken cancellationToken,
+ bool thirdPartyNavigationAllowed = true,
+ bool throwOnHiddenDefinition = false)
+ {
+ var alias = symbol as IAliasSymbol;
+ if (alias != null)
+ {
+ var ns = alias.Target as INamespaceSymbol;
+ if (ns != null && ns.IsGlobalNamespace)
+ {
+ return false;
+ }
+ }
+
+ // VB global import aliases have a synthesized SyntaxTree.
+ // We can't go to the definition of the alias, so use the target type.
+
+ var solution = project.Solution;
+ if (symbol is IAliasSymbol &&
+ NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).All(l => project.Solution.GetDocument(l.SourceTree) == null))
+ {
+ symbol = ((IAliasSymbol)symbol).Target;
+ }
+
+ var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).WaitAndGetResult(cancellationToken);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ symbol = definition ?? symbol;
+
+ if (thirdPartyNavigationAllowed && TryThirdPartyNavigation(symbol, solution))
+ {
+ return true;
+ }
+
+ // If it is a partial method declaration with no body, choose to go to the implementation
+ // that has a method body.
+ if (symbol is IMethodSymbol)
+ {
+ symbol = ((IMethodSymbol)symbol).PartialImplementationPart ?? symbol;
+ }
+
+ var options = project.Solution.Options;
+
+ var preferredSourceLocations = NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).ToArray();
+ var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
+ var title = displayParts.JoinText();
+
+ if (preferredSourceLocations.Length == 0)
+ {
+ // If there are no visible source locations, then tell the host about the symbol and
+ // allow it to navigate to it. This will either navigate to any non-visible source
+ // locations, or it can appropriately deal with metadata symbols for hosts that can go
+ // to a metadata-as-source view.
+
+ var symbolNavigationService = solution.Workspace.Services.GetService();
+ return symbolNavigationService.TryNavigateToSymbol(
+ symbol, project,
+ options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
+ cancellationToken: cancellationToken);
+ }
+ else if (preferredSourceLocations.Length == 1)
+ {
+ var item = NavigableItemFactory.GetItemFromSymbolLocation(
+ solution, symbol, preferredSourceLocations[0],
+ displayTaggedParts: null);
+ return TryGoToSingleLocation(item, options, throwOnHiddenDefinition);
+ }
+ else
+ {
+ // We have multiple viable source locations, so ask the host what to do. Most hosts
+ // will simply display the results to the user and allow them to choose where to
+ // go.
+
+ return TryPresentInFindUsagesPresenter(solution, symbol, streamingPresenters, cancellationToken) ||
+ TryPresentInNavigableItemsPresenter(solution, symbol, presenters, title, preferredSourceLocations);
+ }
+ }
+
+ private static bool TryPresentInFindUsagesPresenter(
+ Solution solution, ISymbol symbol,
+ IEnumerable> streamingPresenters,
+ CancellationToken cancellationToken)
+ {
+ var presenter = GetFindUsagesPresenter(streamingPresenters);
+ if (presenter == null)
+ {
+ return false;
+ }
+
+ var definition = symbol.ToDefinitionItem(solution);
+ var context = presenter.StartSearch(EditorFeaturesResources.Go_to_Definition);
+ try
+ {
+ context.OnDefinitionFoundAsync(definition).Wait(cancellationToken);
+ }
+ finally
+ {
+ context.OnCompletedAsync().Wait(cancellationToken);
+ }
+
+ return true;
+ }
+
+ private static IStreamingFindUsagesPresenter GetFindUsagesPresenter(
+ IEnumerable> streamingPresenters)
+ {
+ try
+ {
+ return streamingPresenters.FirstOrDefault()?.Value;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static bool TryPresentInNavigableItemsPresenter(
+ Solution solution, ISymbol symbol,
+ IEnumerable> presenters,
+ string title, Location[] locations)
+ {
+ var presenter = presenters.FirstOrDefault();
+ if (presenter != null)
+ {
+ var navigableItems = locations.Select(location =>
+ NavigableItemFactory.GetItemFromSymbolLocation(
+ solution, symbol, location,
+ displayTaggedParts: null)).ToImmutableArray();
+
+ presenter.Value.DisplayResult(title, navigableItems);
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool TryGoToSingleLocation(
+ INavigableItem navigableItem, OptionSet options, bool throwOnHiddenDefinition)
+ {
+ var firstItem = navigableItem;
+ var workspace = firstItem.Document.Project.Solution.Workspace;
+ var navigationService = workspace.Services.GetService();
+
+ if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
+ {
+ return navigationService.TryNavigateToSpan(
+ workspace,
+ documentId: firstItem.Document.Id,
+ textSpan: firstItem.SourceSpan,
+ options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
+ }
+ else
+ {
+ if (throwOnHiddenDefinition)
+ {
+ const int E_FAIL = -2147467259;
+ throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
+ {
+ var symbolNavigationService = solution.Workspace.Services.GetService();
+
+ // Notify of navigation so third parties can intercept the navigation
+ return symbolNavigationService.TrySymbolNavigationNotify(symbol, solution);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/GoToDefinition/IGoToDefinitionService.cs b/src/EditorFeatures/Core/GoToDefinition/IGoToDefinitionService.cs
similarity index 100%
rename from src/EditorFeatures/Core/Implementation/GoToDefinition/IGoToDefinitionService.cs
rename to src/EditorFeatures/Core/GoToDefinition/IGoToDefinitionService.cs
diff --git a/src/EditorFeatures/Core/GoToImplementation/AbstractGoToImplementationService.cs b/src/EditorFeatures/Core/GoToImplementation/AbstractGoToImplementationService.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5ec0bf6bcd338bb2db92804bcbaf029a85c7a0c7
--- /dev/null
+++ b/src/EditorFeatures/Core/GoToImplementation/AbstractGoToImplementationService.cs
@@ -0,0 +1,99 @@
+// 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.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Editor.GoToDefinition;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Navigation;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
+{
+ internal abstract class AbstractGoToImplementationService : IGoToImplementationService
+ {
+ private readonly IEnumerable> _navigableItemPresenters;
+
+ public AbstractGoToImplementationService(
+ IEnumerable> navigableItemPresenters)
+ {
+ _navigableItemPresenters = navigableItemPresenters;
+ }
+
+ public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
+ {
+ var result = FindUsagesHelpers.FindImplementationsAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
+ if (result == null)
+ {
+ message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
+ return false;
+ }
+
+ if (result.Value.message != null)
+ {
+ message = result.Value.message;
+ return false;
+ }
+
+ return TryGoToImplementations(
+ result.Value.symbol, result.Value.project,
+ result.Value.implementations, cancellationToken, out message);
+ }
+
+ private bool TryGoToImplementations(
+ ISymbol symbol, Project project, ImmutableArray implementations, CancellationToken cancellationToken, out string message)
+ {
+ if (implementations.Length == 0)
+ {
+ message = EditorFeaturesResources.The_symbol_has_no_implementations;
+ return false;
+ }
+ else if (implementations.Length == 1)
+ {
+ GoToDefinitionHelpers.TryGoToDefinition(
+ implementations.Single(), project, _navigableItemPresenters,
+ SpecializedCollections.EmptyEnumerable>(),
+ cancellationToken);
+ message = null;
+ return true;
+ }
+ else
+ {
+ return TryPresentInNavigableItemsPresenter(symbol, project, implementations, out message);
+ }
+ }
+
+ private bool TryPresentInNavigableItemsPresenter(
+ ISymbol symbol, Project project, ImmutableArray implementations, out string message)
+ {
+ // We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
+ var navigableItems = implementations.SelectMany(
+ implementation => CreateItemsForImplementation(implementation, project.Solution));
+
+ var presenter = _navigableItemPresenters.First();
+
+ var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
+
+ presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
+ message = null;
+ return true;
+ }
+
+ private static IEnumerable CreateItemsForImplementation(ISymbol implementation, Solution solution)
+ {
+ var symbolDisplayService = solution.Workspace.Services.GetLanguageServices(implementation.Language).GetRequiredService();
+
+ return NavigableItemFactory.GetItemsFromPreferredSourceLocations(
+ solution,
+ implementation,
+ displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/GoToImplementation/GoToImplementationCommandHandler.cs b/src/EditorFeatures/Core/GoToImplementation/GoToImplementationCommandHandler.cs
new file mode 100644
index 0000000000000000000000000000000000000000..28611f7a0f28be45ab7952f1a0888cdaaa16a05e
--- /dev/null
+++ b/src/EditorFeatures/Core/GoToImplementation/GoToImplementationCommandHandler.cs
@@ -0,0 +1,146 @@
+// 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.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.Editor.Commands;
+using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
+using Microsoft.CodeAnalysis.Editor.Shared.Options;
+using Microsoft.CodeAnalysis.Notification;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
+{
+ [ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
+ ContentTypeNames.RoslynContentType)]
+ internal partial class GoToImplementationCommandHandler : ICommandHandler
+ {
+ private readonly IWaitIndicator _waitIndicator;
+ private readonly IEnumerable> _streamingPresenters;
+
+ [ImportingConstructor]
+ public GoToImplementationCommandHandler(
+ IWaitIndicator waitIndicator,
+ [ImportMany] IEnumerable> streamingPresenters)
+ {
+ _waitIndicator = waitIndicator;
+ _streamingPresenters = streamingPresenters;
+ }
+
+ public CommandState GetCommandState(GoToImplementationCommandArgs args, Func nextHandler)
+ {
+ // Because this is expensive to compute, we just always say yes
+ return CommandState.Available;
+ }
+
+ public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
+ {
+ var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
+
+ if (caret.HasValue)
+ {
+ var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
+ if (document != null)
+ {
+ ExecuteCommand(document, caret.Value);
+ return;
+ }
+ }
+
+ nextHandler();
+ }
+
+ private void ExecuteCommand(Document document, int caretPosition)
+ {
+ var streamingService = document.GetLanguageService();
+ var synchronousService = document.GetLanguageService();
+
+ var streamingPresenter = GetStreamingPresenter();
+
+ // See if we're running on a host that can provide streaming results.
+ // We'll both need a FAR service that can stream results to us, and
+ // a presenter that can accept streamed results.
+ var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingGoToImplementation, document.Project.Language);
+ var canUseStreamingWindow = streamingEnabled && streamingService != null && streamingPresenter != null;
+ var canUseSynchronousWindow = synchronousService != null;
+
+ if (canUseStreamingWindow || canUseSynchronousWindow)
+ {
+ // We have all the cheap stuff, so let's do expensive stuff now
+ string messageToShow = null;
+ _waitIndicator.Wait(
+ EditorFeaturesResources.Go_To_Implementation,
+ EditorFeaturesResources.Locating_implementations,
+ allowCancel: true,
+ action: context =>
+ {
+ if (canUseStreamingWindow)
+ {
+ StreamingGoToImplementation(
+ document, caretPosition,
+ streamingService, streamingPresenter,
+ context.CancellationToken, out messageToShow);
+ }
+ else
+ {
+ synchronousService.TryGoToImplementation(
+ document, caretPosition, context.CancellationToken, out messageToShow);
+ }
+ });
+
+ if (messageToShow != null)
+ {
+ var notificationService = document.Project.Solution.Workspace.Services.GetService();
+ notificationService.SendNotification(messageToShow,
+ title: EditorFeaturesResources.Go_To_Implementation,
+ severity: NotificationSeverity.Information);
+ }
+ }
+ }
+
+ private void StreamingGoToImplementation(
+ Document document, int caretPosition,
+ IFindUsagesService findUsagesService,
+ IStreamingFindUsagesPresenter streamingPresenter,
+ CancellationToken cancellationToken,
+ out string messageToShow)
+ {
+ // We create our own context object, simply to capture all the definitions reported by
+ // the individual IFindUsagesService. Once we get the results back we'll then decide
+ // what to do with them. If we get only a single result back, then we'll just go
+ // directly to it. Otherwise, we'll present the results in the IStreamingFindUsagesPresenter.
+ var goToImplContext = new SimpleFindUsagesContext(cancellationToken);
+ findUsagesService.FindImplementationsAsync(document, caretPosition, goToImplContext).Wait(cancellationToken);
+
+ // If finding implementations reported a message, then just stop and show that
+ // message to the user.
+ messageToShow = goToImplContext.Message;
+ if (messageToShow != null)
+ {
+ return;
+ }
+
+ var definitionItems = goToImplContext.GetDefinitions();
+
+ streamingPresenter.NavigateToOrPresentItemsAsync(
+ EditorFeaturesResources.Go_To_Implementation, definitionItems).Wait(cancellationToken);
+ }
+
+ private IStreamingFindUsagesPresenter GetStreamingPresenter()
+ {
+ try
+ {
+ return _streamingPresenters.FirstOrDefault()?.Value;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/GoToImplementation/IGoToImplementationService.cs b/src/EditorFeatures/Core/GoToImplementation/IGoToImplementationService.cs
similarity index 100%
rename from src/EditorFeatures/Core/Implementation/GoToImplementation/IGoToImplementationService.cs
rename to src/EditorFeatures/Core/GoToImplementation/IGoToImplementationService.cs
diff --git a/src/EditorFeatures/Core/Host/IDefinitionsAndReferencesPresenter.cs b/src/EditorFeatures/Core/Host/IDefinitionsAndReferencesPresenter.cs
index 47fba57ae8e4cf4a3a616e16ed28e3a9e2970f0b..70256bae4736e07cdb7f5d91c6f713bc0d916ca9 100644
--- a/src/EditorFeatures/Core/Host/IDefinitionsAndReferencesPresenter.cs
+++ b/src/EditorFeatures/Core/Host/IDefinitionsAndReferencesPresenter.cs
@@ -1,6 +1,6 @@
// 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 Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.Host
{
diff --git a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs
index 186d4d96c66b8ec6f59f99b2a2e27b6b8c141637..4c98d241aa257fef01bf1c8ed24cf7ca94573b6b 100644
--- a/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs
+++ b/src/EditorFeatures/Core/Host/IStreamingFindReferencesPresenter.cs
@@ -1,21 +1,82 @@
// 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.Collections.Immutable;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.FindUsages;
+
namespace Microsoft.CodeAnalysis.Editor.Host
{
///
- /// API for hosts to provide if they can present FindReferences results in a streaming manner.
+ /// API for hosts to provide if they can present FindUsages results in a streaming manner.
/// i.e. if they support showing results as they are found instead of after all of the results
/// are found.
///
- internal interface IStreamingFindReferencesPresenter
+ internal interface IStreamingFindUsagesPresenter
{
///
- /// Tells the presenter that a search is starting. The returned
+ /// Tells the presenter that a search is starting. The returned
/// is used to push information about the search into. i.e. when a reference is found
- /// should be called. When the
- /// search completes should be called.
+ /// should be called. When the
+ /// search completes should be called.
/// etc. etc.
///
- FindReferencesContext StartSearch();
+ FindUsagesContext StartSearch(string title);
+ }
+
+ internal static class IStreamingFindUsagesPresenterExtensions
+ {
+ ///
+ /// If there's only a single item, navigates to it. Otherwise, presents all the
+ /// items to the user.
+ ///
+ public static async Task NavigateToOrPresentItemsAsync(
+ this IStreamingFindUsagesPresenter presenter,
+ string title, ImmutableArray items)
+ {
+ // Ignore any definitions that we can't navigate to.
+ var definitions = items.WhereAsArray(d => d.CanNavigateTo());
+
+ // See if there's a third party external item we can navigate to. If so, defer
+ // to that item and finish.
+ var externalItems = definitions.WhereAsArray(d => d.IsExternal);
+ foreach (var item in externalItems)
+ {
+ if (item.TryNavigateTo())
+ {
+ return;
+ }
+ }
+
+ var nonExternalItems = definitions.WhereAsArray(d => !d.IsExternal);
+ if (nonExternalItems.Length == 0)
+ {
+ return;
+ }
+
+ if (nonExternalItems.Length == 1 &&
+ nonExternalItems[0].SourceSpans.Length <= 1)
+ {
+ // There was only one location to navigate to. Just directly go to that location.
+ nonExternalItems[0].TryNavigateTo();
+ return;
+ }
+
+ // We have multiple definitions, or we have definitions with multiple locations.
+ // Present this to the user so they can decide where they want to go to.
+
+ var context = presenter.StartSearch(title);
+ foreach (var definition in nonExternalItems)
+ {
+ await context.OnDefinitionFoundAsync(definition).ConfigureAwait(false);
+ }
+
+ // Note: we don't need to put this in a finally. The only time we might not hit
+ // this is if cancellation or another error gets thrown. In the former case,
+ // that means that a new search has started. We don't care about telling the
+ // context it has completed. In the latter case somethign wrong has happened
+ // and we don't want to run any more code code in this particular context.
+ await context.OnCompletedAsync().ConfigureAwait(false);
+ }
}
}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.cs b/src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.cs
deleted file mode 100644
index c661febe8b19f03bbeaba896db1b8665bd20f782..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/FindReferences/AbstractFindReferencesService.cs
+++ /dev/null
@@ -1,250 +0,0 @@
-// 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.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-using Microsoft.CodeAnalysis.Editor.SymbolMapping;
-using Microsoft.CodeAnalysis.FindReferences;
-using Microsoft.CodeAnalysis.FindSymbols;
-using Microsoft.CodeAnalysis.Navigation;
-using Microsoft.CodeAnalysis.Shared.Extensions;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
-{
- internal abstract partial class AbstractFindReferencesService :
- ForegroundThreadAffinitizedObject, IFindReferencesService, IStreamingFindReferencesService
- {
- private readonly IEnumerable _referenceSymbolPresenters;
- private readonly IEnumerable _navigableItemPresenters;
- private readonly IEnumerable _externalReferencesProviders;
-
- protected AbstractFindReferencesService(
- IEnumerable referenceSymbolPresenters,
- IEnumerable navigableItemPresenters,
- IEnumerable externalReferencesProviders)
- {
- _referenceSymbolPresenters = referenceSymbolPresenters;
- _navigableItemPresenters = navigableItemPresenters;
- _externalReferencesProviders = externalReferencesProviders;
- }
-
- ///
- /// Common helper for both the synchronous and streaming versions of FAR.
- /// It returns the symbol we want to search for and the solution we should
- /// be searching.
- ///
- /// Note that the returned may absolutely *not* be
- /// the same as document.Project.Solution
. This is because
- /// there may be symbol mapping involved (for example in Metadata-As-Source
- /// scenarios).
- ///
- private async Task> GetRelevantSymbolAndProjectAtPositionAsync(
- Document document, int position, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
- if (symbol == null)
- {
- return null;
- }
-
- // If this document is not in the primary workspace, we may want to search for results
- // in a solution different from the one we started in. Use the starting workspace's
- // ISymbolMappingService to get a context for searching in the proper solution.
- var mappingService = document.Project.Solution.Workspace.Services.GetService();
-
- var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
- if (mapping == null)
- {
- return null;
- }
-
- return Tuple.Create(mapping.Symbol, mapping.Project);
- }
-
- ///
- /// Finds references using the externally defined s.
- ///
- private async Task AddExternalReferencesAsync(Document document, int position, ArrayBuilder builder, CancellationToken cancellationToken)
- {
- // CONSIDER: Do the computation in parallel.
- foreach (var provider in _externalReferencesProviders)
- {
- var references = await provider.FindReferencesAsync(document, position, cancellationToken).ConfigureAwait(false);
- if (references != null)
- {
- builder.AddRange(references.WhereNotNull());
- }
- }
- }
-
- private async Task, Solution>> FindReferencedSymbolsAsync(
- Document document, int position, IWaitContext waitContext)
- {
- var cancellationToken = waitContext.CancellationToken;
-
- var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false);
- if (symbolAndProject == null)
- {
- return null;
- }
-
- var symbol = symbolAndProject.Item1;
- var project = symbolAndProject.Item2;
-
- var displayName = GetDisplayName(symbol);
-
- waitContext.Message = string.Format(
- EditorFeaturesResources.Finding_references_of_0, displayName);
-
- var result = await SymbolFinder.FindReferencesAsync(symbol, project.Solution, cancellationToken).ConfigureAwait(false);
-
- return Tuple.Create(result, project.Solution);
- }
-
- private static string GetDisplayName(ISymbol symbol)
- {
- return symbol.IsConstructor() ? symbol.ContainingType.Name : symbol.Name;
- }
-
- public bool TryFindReferences(Document document, int position, IWaitContext waitContext)
- {
- var cancellationToken = waitContext.CancellationToken;
- var workspace = document.Project.Solution.Workspace;
-
- // First see if we have any external navigable item references.
- // If so, we display the results as navigable items.
- var succeeded = TryFindAndDisplayNavigableItemsReferencesAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
- if (succeeded)
- {
- return true;
- }
-
- // Otherwise, fall back to displaying SymbolFinder based references.
- var result = this.FindReferencedSymbolsAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
- return TryDisplayReferences(result);
- }
-
- ///
- /// Attempts to find and display navigable item references, including the references provided by external providers.
- ///
- /// False if there are no external references or display was not successful.
- private async Task TryFindAndDisplayNavigableItemsReferencesAsync(Document document, int position, IWaitContext waitContext)
- {
- var foundReferences = false;
- if (_externalReferencesProviders.Any())
- {
- var cancellationToken = waitContext.CancellationToken;
- var builder = ArrayBuilder.GetInstance();
- await AddExternalReferencesAsync(document, position, builder, cancellationToken).ConfigureAwait(false);
-
- // TODO: Merging references from SymbolFinder and external providers might lead to duplicate or counter-intuitive results.
- // TODO: For now, we avoid merging and just display the results either from SymbolFinder or the external result providers but not both.
- if (builder.Count > 0 && TryDisplayReferences(builder))
- {
- foundReferences = true;
- }
-
- builder.Free();
- }
-
- return foundReferences;
- }
-
- private bool TryDisplayReferences(IEnumerable result)
- {
- if (result != null && result.Any())
- {
- var title = result.First().DisplayTaggedParts.JoinText();
- foreach (var presenter in _navigableItemPresenters)
- {
- presenter.DisplayResult(title, result);
- return true;
- }
- }
-
- return false;
- }
-
- private bool TryDisplayReferences(Tuple, Solution> result)
- {
- if (result != null && result.Item1 != null)
- {
- var solution = result.Item2;
- var factory = solution.Workspace.Services.GetService();
- var definitionsAndReferences = factory.CreateDefinitionsAndReferences(
- solution, result.Item1);
-
- foreach (var presenter in _referenceSymbolPresenters)
- {
- presenter.DisplayResult(definitionsAndReferences);
- return true;
- }
- }
-
- return false;
- }
-
- public async Task FindReferencesAsync(
- Document document, int position, FindReferencesContext context)
- {
- // NOTE: All ConFigureAwaits in this method need to pass 'true' so that
- // we return to the caller's context. that's so the call to
- // CallThirdPartyExtensionsAsync will happen on the UI thread. We need
- // this to maintain the threading guarantee we had around that method
- // from pre-Roslyn days.
- var findReferencesProgress = await FindReferencesWorkerAsync(
- document, position, context).ConfigureAwait(true);
- if (findReferencesProgress == null)
- {
- return;
- }
-
- // After the FAR engine is done call into any third party extensions to see
- // if they want to add results.
- await findReferencesProgress.CallThirdPartyExtensionsAsync().ConfigureAwait(true);
- }
-
- private async Task FindReferencesWorkerAsync(
- Document document, int position, FindReferencesContext context)
- {
- var cancellationToken = context.CancellationToken;
- cancellationToken.ThrowIfCancellationRequested();
-
- // Find the symbol we want to search and the solution we want to search in.
- var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(
- document, position, cancellationToken).ConfigureAwait(false);
- if (symbolAndProject == null)
- {
- return null;
- }
-
- var symbol = symbolAndProject.Item1;
- var project = symbolAndProject.Item2;
-
- var displayName = GetDisplayName(symbol);
- context.SetSearchLabel(displayName);
-
- var progressAdapter = new ProgressAdapter(project.Solution, context);
-
- // Now call into the underlying FAR engine to find reference. The FAR
- // engine will push results into the 'progress' instance passed into it.
- // We'll take those results, massage them, and forward them along to the
- // FindReferencesContext instance we were given.
- await SymbolFinder.FindReferencesAsync(
- SymbolAndProjectId.Create(symbol, project.Id),
- project.Solution,
- progressAdapter,
- documents: null,
- cancellationToken: cancellationToken).ConfigureAwait(false);
-
- return progressAdapter;
- }
- }
-}
diff --git a/src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesNavigableItemResultProvider.cs b/src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesNavigableItemResultProvider.cs
deleted file mode 100644
index 25c3863f1b3cf4dd9547741b486ffc3f3761e5b4..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/FindReferences/IFindReferencesNavigableItemResultProvider.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Navigation;
-
-namespace Microsoft.CodeAnalysis.Editor
-{
- internal interface IFindReferencesResultProvider
- {
- ///
- /// Compute navigable reference items for the symbol referenced or defined at the given position in the given document.
- ///
- Task> FindReferencesAsync(Document document, int position, CancellationToken cancellationToken);
- }
-}
diff --git a/src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionHelpers.cs b/src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionHelpers.cs
deleted file mode 100644
index 7e405419560bf4e403fd3d9f6b4210c4e1e502a9..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/GoToDefinition/GoToDefinitionHelpers.cs
+++ /dev/null
@@ -1,213 +0,0 @@
-// 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.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.FindSymbols;
-using Microsoft.CodeAnalysis.Navigation;
-using Microsoft.CodeAnalysis.Options;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
-{
- internal static class GoToDefinitionHelpers
- {
- public static bool TryGoToDefinition(
- ISymbol symbol,
- Project project,
- IEnumerable> externalDefinitionProviders,
- IEnumerable> presenters,
- CancellationToken cancellationToken,
- bool thirdPartyNavigationAllowed = true,
- bool throwOnHiddenDefinition = false)
- {
- var alias = symbol as IAliasSymbol;
- if (alias != null)
- {
- var ns = alias.Target as INamespaceSymbol;
- if (ns != null && ns.IsGlobalNamespace)
- {
- return false;
- }
- }
-
- // VB global import aliases have a synthesized SyntaxTree.
- // We can't go to the definition of the alias, so use the target type.
-
- var solution = project.Solution;
- if (symbol is IAliasSymbol &&
- NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).All(l => project.Solution.GetDocument(l.SourceTree) == null))
- {
- symbol = ((IAliasSymbol)symbol).Target;
- }
-
- var definition = SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).WaitAndGetResult(cancellationToken);
- cancellationToken.ThrowIfCancellationRequested();
-
- symbol = definition ?? symbol;
-
- if (thirdPartyNavigationAllowed && TryThirdPartyNavigation(symbol, solution))
- {
- return true;
- }
-
- // If it is a partial method declaration with no body, choose to go to the implementation
- // that has a method body.
- if (symbol is IMethodSymbol)
- {
- symbol = ((IMethodSymbol)symbol).PartialImplementationPart ?? symbol;
- }
-
- var options = project.Solution.Options;
-
- var preferredSourceLocations = NavigableItemFactory.GetPreferredSourceLocations(solution, symbol).ToArray();
- var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
- var title = displayParts.JoinText();
-
- if (!preferredSourceLocations.Any())
- {
- // Attempt to find source locations from external definition providers.
- if (thirdPartyNavigationAllowed && externalDefinitionProviders.Any())
- {
- var externalSourceDefinitions = FindExternalDefinitionsAsync(symbol, project, externalDefinitionProviders, cancellationToken).WaitAndGetResult(cancellationToken).ToImmutableArray();
- if (externalSourceDefinitions.Length > 0)
- {
- return TryGoToDefinition(externalSourceDefinitions, title, options, presenters, throwOnHiddenDefinition);
- }
- }
-
- // If there are no visible source locations, then tell the host about the symbol and
- // allow it to navigate to it. This will either navigate to any non-visible source
- // locations, or it can appropriately deal with metadata symbols for hosts that can go
- // to a metadata-as-source view.
-
- var symbolNavigationService = solution.Workspace.Services.GetService();
- return symbolNavigationService.TryNavigateToSymbol(
- symbol, project,
- options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
- cancellationToken: cancellationToken);
- }
-
- var navigableItems = preferredSourceLocations.Select(location =>
- NavigableItemFactory.GetItemFromSymbolLocation(
- solution, symbol, location,
- displayTaggedParts: null)).ToImmutableArray();
- return TryGoToDefinition(navigableItems, title, options, presenters, throwOnHiddenDefinition);
- }
-
- private static bool TryGoToDefinition(
- ImmutableArray navigableItems,
- string title,
- OptionSet options,
- IEnumerable> presenters,
- bool throwOnHiddenDefinition)
- {
- Contract.ThrowIfNull(options);
-
- // If we have a single location, then just navigate to it.
- if (navigableItems.Length == 1 && navigableItems[0].Document != null)
- {
- var firstItem = navigableItems[0];
- var workspace = firstItem.Document.Project.Solution.Workspace;
- var navigationService = workspace.Services.GetService();
-
- if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
- {
- return navigationService.TryNavigateToSpan(
- workspace,
- documentId: firstItem.Document.Id,
- textSpan: firstItem.SourceSpan,
- options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
- }
- else
- {
- if (throwOnHiddenDefinition)
- {
- const int E_FAIL = -2147467259;
- throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
- }
- else
- {
- return false;
- }
- }
- }
- else
- {
- // We have multiple viable source locations, so ask the host what to do. Most hosts
- // will simply display the results to the user and allow them to choose where to
- // go.
-
- if (presenters.Any())
- {
- presenters.First().Value.DisplayResult(title, navigableItems);
- return true;
- }
-
- return false;
- }
- }
-
- public static async Task> FindExternalDefinitionsAsync(Document document, int position, IEnumerable> externalDefinitionProviders, CancellationToken cancellationToken)
- {
- foreach (var definitionProvider in externalDefinitionProviders)
- {
- var definitions = await definitionProvider.Value.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);
- if (definitions != null && definitions.Any())
- {
- var preferredDefinitions = NavigableItemFactory.GetPreferredNavigableItems(document.Project.Solution, definitions);
- if (preferredDefinitions.Any())
- {
- return preferredDefinitions;
- }
- }
- }
-
- return SpecializedCollections.EmptyEnumerable();
- }
-
- public static async Task> FindExternalDefinitionsAsync(ISymbol symbol, Project project, IEnumerable> externalDefinitionProviders, CancellationToken cancellationToken)
- {
- foreach (var definitionProvider in externalDefinitionProviders)
- {
- var definitions = await definitionProvider.Value.FindDefinitionsAsync(project, symbol, cancellationToken).ConfigureAwait(false);
- if (definitions != null && definitions.Any())
- {
- var preferredDefinitions = NavigableItemFactory.GetPreferredNavigableItems(project.Solution, definitions);
- if (preferredDefinitions.Any())
- {
- return preferredDefinitions;
- }
- }
- }
-
- return SpecializedCollections.EmptyEnumerable();
- }
-
- public static bool TryExternalGoToDefinition(Document document, int position, IEnumerable> externalDefinitionProviders, IEnumerable> presenters, CancellationToken cancellationToken)
- {
- var externalDefinitions = FindExternalDefinitionsAsync(document, position, externalDefinitionProviders, cancellationToken).WaitAndGetResult(cancellationToken);
- if (externalDefinitions != null && externalDefinitions.Any())
- {
- var itemsArray = externalDefinitions.ToImmutableArrayOrEmpty();
- var title = itemsArray[0].DisplayTaggedParts.JoinText();
- return TryGoToDefinition(externalDefinitions.ToImmutableArrayOrEmpty(), title, document.Project.Solution.Workspace.Options, presenters, throwOnHiddenDefinition: true);
- }
-
- return false;
- }
-
- private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
- {
- var symbolNavigationService = solution.Workspace.Services.GetService();
-
- // Notify of navigation so third parties can intercept the navigation
- return symbolNavigationService.TrySymbolNavigationNotify(symbol, solution);
- }
- }
-}
diff --git a/src/EditorFeatures/Core/Implementation/GoToDefinition/INavigableDefinitionProvider.cs b/src/EditorFeatures/Core/Implementation/GoToDefinition/INavigableDefinitionProvider.cs
deleted file mode 100644
index e615bd96e7eda7472c6b30a04f5d569125586b33..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/GoToDefinition/INavigableDefinitionProvider.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Navigation;
-
-namespace Microsoft.CodeAnalysis.Editor
-{
- internal interface INavigableDefinitionProvider
- {
- ///
- /// Find definitions for the symbol referenced or defined at the given position in the given document.
- ///
- Task> FindDefinitionsAsync(Document document, int position, CancellationToken cancellationToken);
-
- ///
- /// Find definitions for the given symbol in the context of the given project.
- ///
- Task> FindDefinitionsAsync(Project project, ISymbol symbol, CancellationToken cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/src/EditorFeatures/Core/Implementation/GoToImplementation/AbstractGoToImplementationService.cs b/src/EditorFeatures/Core/Implementation/GoToImplementation/AbstractGoToImplementationService.cs
deleted file mode 100644
index f5b1c666dd0e52347a1169d4aa92dfae18fe649e..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/GoToImplementation/AbstractGoToImplementationService.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-// 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.Generic;
-using System.Linq;
-using System.Threading;
-using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.SymbolMapping;
-using Microsoft.CodeAnalysis.FindSymbols;
-using Microsoft.CodeAnalysis.LanguageServices;
-using Microsoft.CodeAnalysis.Navigation;
-using Microsoft.CodeAnalysis.Shared.Extensions;
-using Roslyn.Utilities;
-
-namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
-{
- internal abstract class AbstractGoToImplementationService : IGoToImplementationService
- {
- private readonly IEnumerable> _navigableItemPresenters;
- private readonly IEnumerable> _externalDefinitionProviders;
-
- public AbstractGoToImplementationService(
- IEnumerable> navigableItemPresenters,
- IEnumerable> externalDefinitionProviders)
- {
- _navigableItemPresenters = navigableItemPresenters;
- _externalDefinitionProviders = externalDefinitionProviders;
- }
-
- public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
- {
- var symbol = SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
- if (symbol != null)
- {
- // Map the symbol if necessary back to the originating workspace if we're invoking from something
- // like metadata as source
- var mappingService = document.Project.Solution.Workspace.Services.GetRequiredService();
- var mapping = mappingService.MapSymbolAsync(document, symbol, cancellationToken).WaitAndGetResult(cancellationToken);
-
- if (mapping != null)
- {
- return TryGoToImplementationOnMappedSymbol(mapping, cancellationToken, out message);
- }
- }
- else
- {
- return TryExternalGotoDefinition(document, position, cancellationToken, out message);
- }
-
- message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
- return false;
- }
-
- private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, CancellationToken cancellationToken, out string message)
- {
- if (mapping.Symbol.IsInterfaceType() || mapping.Symbol.IsImplementableMember())
- {
- var implementations =
- SymbolFinder.FindImplementationsAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
- .WaitAndGetResult(cancellationToken);
-
- // It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method
- // in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
- // is to just use a HashSet.
- var implementationsAndOverrides = new HashSet();
-
- foreach (var implementation in implementations)
- {
- implementationsAndOverrides.Add(implementation);
-
- // FindImplementationsAsync will only return the base virtual/abstract method, not that method and the overrides
- // of the method. We should also include those.
- if (implementation.IsOverridable())
- {
- implementationsAndOverrides.AddRange(
- SymbolFinder.FindOverridesAsync(implementation, mapping.Solution, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken));
- }
- }
-
- return TryGoToImplementations(implementationsAndOverrides, mapping, cancellationToken, out message);
- }
- else if ((mapping.Symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
- {
- var implementations =
- SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
- .WaitAndGetResult(cancellationToken)
- .Concat(mapping.Symbol);
-
- return TryGoToImplementations(implementations, mapping, cancellationToken, out message);
- }
- else if (mapping.Symbol.IsOverridable())
- {
- var implementations =
- SymbolFinder.FindOverridesAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
- .WaitAndGetResult(cancellationToken)
- .Concat(mapping.Symbol);
-
- return TryGoToImplementations(implementations, mapping, cancellationToken, out message);
- }
- else
- {
- // This is something boring like a regular method or type, so we'll just go there directly
- if (GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(mapping.Symbol, mapping.Project, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken))
- {
- message = null;
- return true;
- }
- else
- {
- message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
- return false;
- }
- }
- }
-
- private bool TryGoToImplementations(IEnumerable candidateImplementations, SymbolMappingResult mapping, CancellationToken cancellationToken, out string message)
- {
- var implementations = candidateImplementations
- .Where(s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource))
- .ToList();
-
- if (implementations.Count == 0)
- {
- message = EditorFeaturesResources.The_symbol_has_no_implementations;
- return false;
- }
- else if (implementations.Count == 1)
- {
- GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(implementations.Single(), mapping.Project, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken);
- message = null;
- return true;
- }
- else
- {
- // We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
- var navigableItems = implementations.SelectMany(
- implementation => CreateItemsForImplementation(implementation, mapping.Solution));
-
- var presenter = _navigableItemPresenters.First();
-
- var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(mapping.Project, mapping.Symbol);
-
- presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
- message = null;
- return true;
- }
- }
-
- private static IEnumerable CreateItemsForImplementation(ISymbol implementation, Solution solution)
- {
- var symbolDisplayService = solution.Workspace.Services.GetLanguageServices(implementation.Language).GetRequiredService();
-
- return NavigableItemFactory.GetItemsFromPreferredSourceLocations(
- solution,
- implementation,
- displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText());
- }
-
- private bool TryExternalGotoDefinition(Document document, int position, CancellationToken cancellationToken, out string message)
- {
- if (GoToDefinition.GoToDefinitionHelpers.TryExternalGoToDefinition(document, position, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken))
- {
- message = null;
- return true;
- }
- else
- {
- message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
- return false;
- }
- }
- }
-}
diff --git a/src/EditorFeatures/Core/Implementation/GoToImplementation/GoToImplementationCommandHandler.cs b/src/EditorFeatures/Core/Implementation/GoToImplementation/GoToImplementationCommandHandler.cs
deleted file mode 100644
index cb90c5479943160c137e67cf36854c6ed74fd046..0000000000000000000000000000000000000000
--- a/src/EditorFeatures/Core/Implementation/GoToImplementation/GoToImplementationCommandHandler.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// 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.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Commands;
-using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
-using Microsoft.CodeAnalysis.Notification;
-using Microsoft.CodeAnalysis.Text;
-
-namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
-{
- [ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
- ContentTypeNames.RoslynContentType)]
- internal sealed class GoToImplementationCommandHandler : ICommandHandler
- {
- private readonly IWaitIndicator _waitIndicator;
-
- [ImportingConstructor]
- public GoToImplementationCommandHandler(
- IWaitIndicator waitIndicator)
- {
- _waitIndicator = waitIndicator;
- }
-
- public CommandState GetCommandState(GoToImplementationCommandArgs args, Func nextHandler)
- {
- // Because this is expensive to compute, we just always say yes
- return CommandState.Available;
- }
-
- public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
- {
- var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
-
- if (caret.HasValue)
- {
- var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
- if (document != null)
- {
- var service = document.Project.LanguageServices.GetService();
-
- if (service != null)
- {
- // We have all the cheap stuff, so let's do expensive stuff now
- string messageToShow = null;
- bool succeeded = false;
- _waitIndicator.Wait(
- EditorFeaturesResources.Go_To_Implementation,
- EditorFeaturesResources.Locating_implementations,
- allowCancel: true,
- action: context => succeeded = service.TryGoToImplementation(document, caret.Value, context.CancellationToken, out messageToShow));
-
- if (messageToShow != null)
- {
- var notificationService = document.Project.Solution.Workspace.Services.GetService();
- notificationService.SendNotification(messageToShow,
- title: EditorFeaturesResources.Go_To_Implementation,
- severity: NotificationSeverity.Information);
- }
- }
-
- return;
- }
- }
-
- nextHandler();
- }
- }
-}
diff --git a/src/EditorFeatures/Core/Shared/Options/FeatureOnOffOptions.cs b/src/EditorFeatures/Core/Shared/Options/FeatureOnOffOptions.cs
index 65f134c0b46f188383c56a5aafef0b3895a7eba6..72cb18a4a0c0fd218e4b961fc52055ca58981560 100644
--- a/src/EditorFeatures/Core/Shared/Options/FeatureOnOffOptions.cs
+++ b/src/EditorFeatures/Core/Shared/Options/FeatureOnOffOptions.cs
@@ -87,9 +87,15 @@ internal static class FeatureOnOffOptions
/// implemented this feature yet.
///
[ExportOption]
- public static readonly PerLanguageOption RefactoringVerification = new PerLanguageOption(nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false);
+ public static readonly PerLanguageOption RefactoringVerification = new PerLanguageOption(
+ nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false);
[ExportOption]
- public static readonly PerLanguageOption StreamingFindReferences = new PerLanguageOption(nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true);
+ public static readonly PerLanguageOption StreamingFindReferences = new PerLanguageOption(
+ nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true);
+
+ [ExportOption]
+ public static readonly PerLanguageOption StreamingGoToImplementation = new PerLanguageOption(
+ nameof(FeatureOnOffOptions), nameof(StreamingGoToImplementation), defaultValue: true);
}
-}
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Test/EditorServicesTest.csproj b/src/EditorFeatures/Test/EditorServicesTest.csproj
index faaa7b0c7663b56ec6ea79110be891843edf6cc5..58cf8a38071f6351ab6740984210837c2ee22aa4 100644
--- a/src/EditorFeatures/Test/EditorServicesTest.csproj
+++ b/src/EditorFeatures/Test/EditorServicesTest.csproj
@@ -249,6 +249,7 @@
+
diff --git a/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs b/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs
index 49f480e0202a2d629abab76c318621cbbe2fe455..78f745a22a5cbff531691fb01428ee698ed117f6 100644
--- a/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs
+++ b/src/EditorFeatures/Test/FindReferences/FindReferencesCommandHandlerTests.cs
@@ -4,11 +4,11 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Commands;
+using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.FindReferences;
using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Text;
using Roslyn.Test.Utilities;
@@ -29,7 +29,7 @@ public async Task TestFindReferencesSynchronousCall()
var handler = new FindReferencesCommandHandler(
TestWaitIndicator.Default,
SpecializedCollections.SingletonEnumerable(findReferencesPresenter),
- SpecializedCollections.EmptyEnumerable>(),
+ SpecializedCollections.EmptyEnumerable>(),
workspace.ExportProvider.GetExports());
var textView = workspace.Documents[0].GetTextView();
diff --git a/src/EditorFeatures/Test/FindReferences/MockDefinitionsAndReferencesPresenter.cs b/src/EditorFeatures/Test/FindReferences/MockDefinitionsAndReferencesPresenter.cs
index 7cb45cb0b2a38a11990e3d2a066ff6bd6dffb7bb..3b9f9ed7997734f9bcd63aef03c945097574ec6e 100644
--- a/src/EditorFeatures/Test/FindReferences/MockDefinitionsAndReferencesPresenter.cs
+++ b/src/EditorFeatures/Test/FindReferences/MockDefinitionsAndReferencesPresenter.cs
@@ -1,7 +1,7 @@
// 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 Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
{
diff --git a/src/EditorFeatures/Test/Workspaces/WorkspaceTests.cs b/src/EditorFeatures/Test/Workspaces/WorkspaceTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f08b6b5a5eccf36d22d441fe9e2b7b9c2d0f0604
--- /dev/null
+++ b/src/EditorFeatures/Test/Workspaces/WorkspaceTests.cs
@@ -0,0 +1,30 @@
+// 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.Generic;
+using System.Collections.Immutable;
+using System.Threading;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces;
+using Microsoft.CodeAnalysis.Host;
+using Roslyn.Test.Utilities;
+using Roslyn.Utilities;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
+{
+ public class WorkspaceTests
+ {
+ [Fact]
+ public void TestDefaultCompositionIncludesFeaturesLayer()
+ {
+ var ws = new AdhocWorkspace();
+
+ var csservice = ws.Services.GetLanguageServices(LanguageNames.CSharp).GetService();
+ Assert.NotNull(csservice);
+
+ var vbservice = ws.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService();
+ Assert.NotNull(vbservice);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb
index e2bddc15afcb0273dcfc71672989dd916b626fd3..7f7189b3e5ff9b55e31c081ec317e6fd16d32e72 100644
--- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb
+++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb
@@ -4,11 +4,12 @@ Imports System.Collections.Immutable
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
-Imports Microsoft.CodeAnalysis.FindReferences
+Imports Microsoft.CodeAnalysis.FindUsages
Imports Microsoft.CodeAnalysis.FindSymbols
Imports Microsoft.CodeAnalysis.Text
Imports Roslyn.Utilities
Imports Xunit.Abstractions
+Imports Microsoft.CodeAnalysis.Editor.FindUsages
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Partial Public Class FindReferencesTests
@@ -38,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Dim startDocument = workspace.CurrentSolution.GetDocument(cursorDocument.Id)
Assert.NotNull(startDocument)
- Dim findRefsService = startDocument.GetLanguageService(Of IStreamingFindReferencesService)
+ Dim findRefsService = startDocument.GetLanguageService(Of IFindUsagesService)
Dim context = New TestContext()
Await findRefsService.FindReferencesAsync(startDocument, cursorPosition, context)
@@ -109,7 +110,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
End Structure
Private Class TestContext
- Inherits FindReferencesContext
+ Inherits FindUsagesContext
Private ReadOnly gate As Object = New Object()
diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb
index c573ef303ca8dfce9e294c7d214184fdd7cc9bb5..0201a0de36de462ee9a4a2b2b72408fc40a3b96a 100644
--- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb
+++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb
@@ -3,7 +3,7 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.Host
-Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
+Imports Microsoft.CodeAnalysis.Editor.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb
index 6960169ebb422790836879e091d6d0d7837dfc5e..023a56338fa4521776e411871873ff6565a61939 100644
--- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb
+++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb
@@ -11,10 +11,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Public Class GoToDefinitionTests
Private Function TestAsync(workspaceDefinition As XElement, Optional expectedResult As Boolean = True) As Tasks.Task
Return GoToTestHelpers.TestAsync(workspaceDefinition, expectedResult,
- Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
+ Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim goToDefService = If(document.Project.Language = LanguageNames.CSharp,
- DirectCast(New CSharpGoToDefinitionService(presenters, externalDefinitionProviders), IGoToDefinitionService),
- New VisualBasicGoToDefinitionService(presenters, externalDefinitionProviders))
+ DirectCast(New CSharpGoToDefinitionService(presenters, {}), IGoToDefinitionService),
+ New VisualBasicGoToDefinitionService(presenters, {}))
Return goToDefService.TryGoToDefinition(document, cursorPosition, CancellationToken.None)
End Function)
diff --git a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb
index 1823986f919ab0bb8a07adad1f4d8aebd6e69926..5171e7916671174754f45daad5f66c447d84e6fd 100644
--- a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb
+++ b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb
@@ -11,10 +11,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToImplementation
Public Class GoToImplementationTests
Private Function TestAsync(workspaceDefinition As XElement, Optional shouldSucceed As Boolean = True) As Tasks.Task
Return GoToTestHelpers.TestAsync(workspaceDefinition, shouldSucceed,
- Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
+ Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim service = If(document.Project.Language = LanguageNames.CSharp,
- DirectCast(New CSharpGoToImplementationService(presenters, externalDefinitionProviders), IGoToImplementationService),
- New VisualBasicGoToImplementationService(presenters, externalDefinitionProviders))
+ DirectCast(New CSharpGoToImplementationService(presenters), IGoToImplementationService),
+ New VisualBasicGoToImplementationService(presenters))
Dim message As String = Nothing
Return service.TryGoToImplementation(document, cursorPosition, CancellationToken.None, message)
diff --git a/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs
index c9728bc3d93ea3ffac0d6a182223c8e5fc582942..7f80d9b0f32dc7eb24afdf266dddca3978a4b717 100644
--- a/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs
+++ b/src/EditorFeatures/TestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs
@@ -530,10 +530,13 @@ protected virtual IList MassageActions(IList actions)
protected static IList FlattenActions(IEnumerable codeActions)
{
return codeActions?.SelectMany(a => a.NestedCodeActions.Length > 0
- ? a.NestedCodeActions.ToArray()
+ ? a.NestedCodeActions.ToArray()
: new[] { a }).ToList();
}
+ protected (OptionKey, object) SingleOption(Option option, bool enabled)
+ => (new OptionKey(option), enabled);
+
protected (OptionKey, object) SingleOption(Option> option, bool enabled, NotificationOption notification)
=> SingleOption(option, new CodeStyleOption(enabled, notification));
diff --git a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/GoToTestHelpers.vb b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/GoToTestHelpers.vb
index 0ddd4f430ab0fca4d750d73ce2ecb9b7be243706..98c4a88813562a0ba78146831f5095de15b87b5a 100644
--- a/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/GoToTestHelpers.vb
+++ b/src/EditorFeatures/TestUtilities2/Utilities/GoToHelpers/GoToTestHelpers.vb
@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Public ReadOnly ExportProvider As ExportProvider = MinimalTestExportProvider.CreateExportProvider(Catalog)
- Public Async Function TestAsync(workspaceDefinition As XElement, expectedResult As Boolean, executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of INavigableItemsPresenter)), IEnumerable(Of Lazy(Of INavigableDefinitionProvider)), Boolean)) As System.Threading.Tasks.Task
+ Public Async Function TestAsync(workspaceDefinition As XElement, expectedResult As Boolean, executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of INavigableItemsPresenter)), Boolean)) As System.Threading.Tasks.Task
Using workspace = Await TestWorkspace.CreateAsync(workspaceDefinition, exportProvider:=ExportProvider)
Dim solution = workspace.CurrentSolution
Dim cursorDocument = workspace.Documents.First(Function(d) d.CursorPosition.HasValue)
@@ -41,7 +41,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Dim presenter = New MockNavigableItemsPresenter(Sub(i) items = i)
Dim presenters = {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}
- Dim actualResult = executeOnDocument(document, cursorPosition, presenters, {})
+ Dim actualResult = executeOnDocument(document, cursorPosition, presenters)
Assert.Equal(expectedResult, actualResult)
diff --git a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
index e864ee5d5bffaa32eb73346e4325144cc7c947fb..52beba08ac7efbe6323a07b6eea3a546479ed10f 100644
--- a/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
+++ b/src/EditorFeatures/VisualBasic/BasicEditorFeatures.vbproj
@@ -100,6 +100,7 @@
+
diff --git a/src/EditorFeatures/VisualBasic/FindReferences/VisualBasicFindReferencesService.vb b/src/EditorFeatures/VisualBasic/FindReferences/VisualBasicFindReferencesService.vb
index b61586da6d6cef57333bb7aef74cd38523b01ae7..1ac2e725b8f0a8b245f0662d60741789aa2bb968 100644
--- a/src/EditorFeatures/VisualBasic/FindReferences/VisualBasicFindReferencesService.vb
+++ b/src/EditorFeatures/VisualBasic/FindReferences/VisualBasicFindReferencesService.vb
@@ -1,8 +1,8 @@
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.FindReferences
Imports Microsoft.CodeAnalysis.Editor.Host
-Imports Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindReferences
@@ -12,21 +12,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindReferences
Protected Sub New( referencedSymbolsPresenters As IEnumerable(Of IDefinitionsAndReferencesPresenter),
- navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter),
- externalReferencesProviders As IEnumerable(Of IFindReferencesResultProvider))
- MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
- End Sub
- End Class
-
-
- Friend Class VisualBasicStreamingFindReferencesService
- Inherits AbstractFindReferencesService
-
-
- Protected Sub New( referencedSymbolsPresenters As IEnumerable(Of IDefinitionsAndReferencesPresenter),
- navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter),
- externalReferencesProviders As IEnumerable(Of IFindReferencesResultProvider))
- MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
+ navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter))
+ MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters)
End Sub
End Class
End Namespace
\ No newline at end of file
diff --git a/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb
new file mode 100644
index 0000000000000000000000000000000000000000..217963bf5dcd2badc1c6b4574d5b766303631dd7
--- /dev/null
+++ b/src/EditorFeatures/VisualBasic/FindUsages/VisualBasicFindUsagesService.vb
@@ -0,0 +1,12 @@
+' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.FindUsages
+Imports Microsoft.CodeAnalysis.Host.Mef
+
+Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindUsages
+
+ Friend Class VisualBasicFindUsagesService
+ Inherits AbstractFindUsagesService
+ End Class
+End Namespace
\ No newline at end of file
diff --git a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb
index 54462222f1c2e32ae182944ddc03f40b63836570..3807bca6b8b658b8b3dcc6b3e267f1a6bf67d53e 100644
--- a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb
+++ b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb
@@ -1,8 +1,8 @@
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.Host
-Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities
@@ -12,12 +12,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition
Inherits AbstractGoToDefinitionService
- Public Sub New( presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
- MyBase.New(presenters, externalDefinitionProviders)
+ Public Sub New( presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)),
+ streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
+ MyBase.New(presenters, streamingPresenters)
End Sub
Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol
Return symbol.FindRelatedExplicitlyDeclaredSymbol(compilation)
End Function
End Class
-End Namespace
+End Namespace
\ No newline at end of file
diff --git a/src/EditorFeatures/VisualBasic/GoToImplementation/VisualBasicGoToImplementationService.vb b/src/EditorFeatures/VisualBasic/GoToImplementation/VisualBasicGoToImplementationService.vb
index 23b4964fc1f6a323c5491459e4c5863138c81f9d..122cecd148fb4b19d23de96721de2703af8038f8 100644
--- a/src/EditorFeatures/VisualBasic/GoToImplementation/VisualBasicGoToImplementationService.vb
+++ b/src/EditorFeatures/VisualBasic/GoToImplementation/VisualBasicGoToImplementationService.vb
@@ -1,8 +1,8 @@
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
+Imports Microsoft.CodeAnalysis.Editor.GoToImplementation
Imports Microsoft.CodeAnalysis.Editor.Host
-Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToImplementation
@@ -11,8 +11,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToImplementation
Inherits AbstractGoToImplementationService
- Public Sub New( presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
- MyBase.New(presenters, externalDefinitionProviders)
+ Public Sub New( presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
+ MyBase.New(presenters)
End Sub
End Class
-End Namespace
+End Namespace
\ No newline at end of file
diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionServiceTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionServiceTests.vb
index 832e5f97463511e110587cc1e6e75a8412bf9d74..f204c7e1f02ab38deecc71b2a118b1a078ea3a9d 100644
--- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionServiceTests.vb
+++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionServiceTests.vb
@@ -8,12 +8,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion
Public Class CompletionServiceTests
Public Sub AcquireCompletionService()
- Dim hostServices = MefHostServices.Create(
- MefHostServices.DefaultAssemblies.Concat({
- GetType(CompletionService).Assembly,
- GetType(VisualBasicCompletionService).Assembly}))
-
- Dim workspace = New AdhocWorkspace(hostServices)
+ Dim workspace = New AdhocWorkspace()
Dim document = workspace _
.AddProject("TestProject", LanguageNames.VisualBasic) _
diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs
index 7b20dcdf7e9aec116327803b010517ad3a2cac6f..cd4179ae33f2af2b5807837c94175b1c21462aae 100644
--- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs
+++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs
@@ -21,8 +21,13 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context
return true;
}
- return !context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword)
- && InMemberDeclarationContext(position, context, cancellationToken);
+ if (context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword))
+ {
+ return false;
+ }
+
+ return InMemberDeclarationContext(position, context, cancellationToken)
+ || context.SyntaxTree.IsLocalFunctionDeclarationContext(position, cancellationToken);
}
private static bool InMemberDeclarationContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs
index a78636245a1044ce3465fe4c24d41dc7a6c83bcc..079c0cd9ffcca43dc328b85303961d544eb0608d 100644
--- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs
+++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs
@@ -45,7 +45,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context
IsUnsafeParameterTypeContext(context) ||
IsUnsafeCastTypeContext(context) ||
IsUnsafeDefaultExpressionContext(context, cancellationToken) ||
- context.IsFixedVariableDeclarationContext;
+ context.IsFixedVariableDeclarationContext ||
+ context.SyntaxTree.IsLocalFunctionDeclarationContext(position, cancellationToken);
}
private bool IsUnsafeDefaultExpressionContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
diff --git a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpTypeStyleDiagnosticAnalyzerBase.State.cs b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpTypeStyleDiagnosticAnalyzerBase.State.cs
index 6b4bb285e62d65ee7bf5ccd4b5bb87519483fa68..d8803c8e221924034ffdbd483160c04c9dc7ea9b 100644
--- a/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpTypeStyleDiagnosticAnalyzerBase.State.cs
+++ b/src/Features/CSharp/Portable/Diagnostics/Analyzers/CSharpTypeStyleDiagnosticAnalyzerBase.State.cs
@@ -97,12 +97,11 @@ private bool IsPredefinedTypeInDeclaration(SyntaxNode declarationStatement)
private bool IsInferredPredefinedType(SyntaxNode declarationStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
- TypeSyntax typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
+ var typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
- return typeSyntax != null
- ? typeSyntax.IsTypeInferred(semanticModel) &&
- semanticModel.GetTypeInfo(typeSyntax).Type?.IsSpecialType() == true
- : false;
+ return typeSyntax != null &&
+ typeSyntax.IsTypeInferred(semanticModel) &&
+ semanticModel.GetTypeInfo(typeSyntax).Type?.IsSpecialType() == true;
}
private TypeSyntax GetTypeSyntaxFromDeclaration(SyntaxNode declarationStatement)
diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs
index bf9a7da9cc010e77e3074828f607af64fbc6aee6..f82bdda31da4253f657dfa0ea63d516b2c2dee16 100644
--- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs
+++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs
@@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
@@ -381,13 +382,12 @@ private OperationStatus CheckActiveStatements(IEnumerable state
// return survived var decls
if (list.Count > 0)
{
- yield return
- SyntaxFactory.LocalDeclarationStatement(
- declarationStatement.Modifiers,
- SyntaxFactory.VariableDeclaration(
- declarationStatement.Declaration.Type,
- SyntaxFactory.SeparatedList(list)),
- declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
+ yield return SyntaxFactory.LocalDeclarationStatement(
+ declarationStatement.Modifiers,
+ SyntaxFactory.VariableDeclaration(
+ declarationStatement.Declaration.Type,
+ SyntaxFactory.SeparatedList(list)),
+ declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
triviaList.Clear();
}
@@ -603,11 +603,19 @@ protected override StatementSyntax CreateAssignmentExpressionStatement(SyntaxTok
protected override StatementSyntax CreateDeclarationStatement(
VariableInfo variable,
- CancellationToken cancellationToken,
- ExpressionSyntax initialValue = null)
+ ExpressionSyntax initialValue,
+ CancellationToken cancellationToken)
{
+ // Convert to 'var' if appropriate. Note: because we're extracting out
+ // to a method, the initialValue will be a method-call. Types are not
+ // apperant with method calls (i.e. as opposed to a 'new XXX()' expression,
+ // where the type is apperant).
var type = variable.GetVariableType(this.SemanticDocument);
- var typeNode = type.GenerateTypeSyntax();
+ var typeNode = initialValue == null
+ ? type.GenerateTypeSyntax()
+ : type.GenerateTypeSyntaxOrVar(
+ this.SemanticDocument.Document.Project.Solution.Options,
+ typeIsApperant: false);
var equalsValueClause = initialValue == null ? null : SyntaxFactory.EqualsValueClause(value: initialValue);
diff --git a/src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs b/src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
index c729a17c3f3bb500bfd92592b81202c0cf90ccb8..774ada6019056cf416720f7aaaa1b5f4b67f3a4f 100644
--- a/src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
+++ b/src/Features/CSharp/Portable/InlineDeclaration/CSharpInlineDeclarationCodeFixProvider.cs
@@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
@@ -47,22 +48,21 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
// Note: if using 'var' would cause a problem, we will use the actual type
// of hte local. This is necessary in some cases (for example, when the
// type of the out-var-decl affects overload resolution or generic instantiation).
- var useVarWhenDeclaringLocals = options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals);
- var useImplicitTypeForIntrinsicTypes = options.GetOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes).Value;
foreach (var diagnostic in diagnostics)
{
cancellationToken.ThrowIfCancellationRequested();
- await AddEditsAsync(document, editor, diagnostic,
- useVarWhenDeclaringLocals, useImplicitTypeForIntrinsicTypes,
- cancellationToken).ConfigureAwait(false);
+ await AddEditsAsync(
+ document, editor, diagnostic,
+ options, cancellationToken).ConfigureAwait(false);
}
}
private async Task AddEditsAsync(
Document document, SyntaxEditor editor, Diagnostic diagnostic,
- bool useVarWhenDeclaringLocals, bool useImplicitTypeForIntrinsicTypes, CancellationToken cancellationToken)
+ OptionSet options, CancellationToken cancellationToken)
{
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
// Recover the nodes we care about.
@@ -121,8 +121,15 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
}
// get the type that we want to put in the out-var-decl based on the user's options.
- // i.e. prefer 'out var' if that is what the user wants.
- var newType = this.GetDeclarationType(declaration.Type, useVarWhenDeclaringLocals, useImplicitTypeForIntrinsicTypes);
+ // i.e. prefer 'out var' if that is what the user wants. Note: if we have:
+ //
+ // Method(out var x)
+ //
+ // Then the type is not-apperant, and we shoudl not use var if the user only wants
+ // it for apperant types
+
+ var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator);
+ var newType = local.Type.GenerateTypeSyntaxOrVar(options, typeIsApperant: false);
var declarationExpression = GetDeclarationExpression(
sourceText, identifier, newType, singleDeclarator ? null : declarator);
@@ -131,11 +138,9 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
var semanticsChanged = await SemanticsChangedAsync(
document, declaration, invocationOrCreation, newType,
identifier, declarationExpression, cancellationToken).ConfigureAwait(false);
- if (semanticsChanged)
+ if (semanticsChanged && newType.IsVar)
{
// Switching to 'var' changed semantics. Just use the original type of the local.
- var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
- var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator);
// If the user originally wrote it something other than 'var', then use what they
// wrote. Otherwise, synthesize the actual type of the local.
diff --git a/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider.cs b/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider.cs
index b4fddb5cfd78765a9173f5ca564f0e6682983914..6519a90b1696fc7b1aeee9406eff72de5cf67d7a 100644
--- a/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider.cs
+++ b/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider.cs
@@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionInitializer), Shared]
internal class CSharpUseCollectionInitializerCodeFixProvider :
AbstractUseCollectionInitializerCodeFixProvider<
+ SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
diff --git a/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs
index 8da1bbc875fa8df5be6f62a91b25cabfd509935b..17e2af307102979fefc40eb887770ffe26f33976 100644
--- a/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs
+++ b/src/Features/CSharp/Portable/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs
@@ -1,6 +1,5 @@
// 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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
diff --git a/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerCodeFixProvider.cs b/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerCodeFixProvider.cs
index 3d0cb459a92c9600cd2ec6ed2a1ac5a2da777b26..5fc0c7acc2e1757a2d0c2282b55a94d9c4dbdea7 100644
--- a/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerCodeFixProvider.cs
+++ b/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerCodeFixProvider.cs
@@ -5,8 +5,6 @@
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Formatting;
-using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.UseObjectInitializer;
namespace Microsoft.CodeAnalysis.CSharp.UseObjectInitializer
@@ -14,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseObjectInitializer
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseObjectInitializer), Shared]
internal class CSharpUseObjectInitializerCodeFixProvider :
AbstractUseObjectInitializerCodeFixProvider<
+ SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
@@ -23,7 +22,7 @@ internal class CSharpUseObjectInitializerCodeFixProvider :
{
protected override ObjectCreationExpressionSyntax GetNewObjectCreation(
ObjectCreationExpressionSyntax objectCreation,
- ImmutableArray> matches)
+ ImmutableArray> matches)
{
var openBrace = SyntaxFactory.Token(SyntaxKind.OpenBraceToken)
.WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed);
@@ -35,7 +34,7 @@ internal class CSharpUseObjectInitializerCodeFixProvider :
}
private SeparatedSyntaxList CreateExpressions(
- ImmutableArray> matches)
+ ImmutableArray> matches)
{
var nodesAndTokens = new List();
for (int i = 0; i < matches.Length; i++)
diff --git a/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerDiagnosticAnalyzer.cs
index 0826cb3b849914813be99a64303ee7f61d1187df..b261e4dbd8199030c4f2f71cea5f28ab85232d21 100644
--- a/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerDiagnosticAnalyzer.cs
+++ b/src/Features/CSharp/Portable/UseObjectInitializer/CSharpUseObjectInitializerDiagnosticAnalyzer.cs
@@ -1,6 +1,5 @@
// 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 Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
diff --git a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs
index 2d942b51447900f8e324d4a19e8d2016ce337aa2..0e09bbdb673d60653db2fad7598ddc7accea22e2 100644
--- a/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs
+++ b/src/Features/Core/Portable/Completion/CompletionServiceWithProviders.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -212,7 +213,7 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var defaultItemSpan = this.GetDefaultCompletionListSpan(text, caretPosition);
- options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);;
+ options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var providers = GetFilteredProviders(roles, trigger, options);
var completionProviderToIndex = GetCompletionProviderToIndex(providers);
@@ -220,38 +221,43 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var triggeredProviders = ImmutableArray.Empty;
switch (trigger.Kind)
{
- case CompletionTriggerKind.Insertion:
- case CompletionTriggerKind.Deletion:
- if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options))
- {
- triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
- if (triggeredProviders.Length == 0)
+ case CompletionTriggerKind.Insertion:
+ case CompletionTriggerKind.Deletion:
+ if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options))
{
- triggeredProviders = providers;
+ triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
+ if (triggeredProviders.Length == 0)
+ {
+ triggeredProviders = providers;
+ }
}
- }
- break;
- default:
- triggeredProviders = providers;
- break;
+ break;
+ default:
+ triggeredProviders = providers;
+ break;
}
- // Now, ask all the triggered providers if they can provide a group.
- var completionContexts = new List();
- foreach (var provider in triggeredProviders)
+ // Now, ask all the triggered providers, in parallel, to populate a completion context.
+ // Note: we keep any context with items *or* with a suggested item.
+ var triggeredCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
+ document, caretPosition, trigger, options,
+ defaultItemSpan, triggeredProviders,
+ cancellationToken).ConfigureAwait(false);
+
+ // If we didn't even get any back with items, then there's nothing to do.
+ // i.e. if only got items back that had only suggestion items, then we don't
+ // want to show any completion.
+ if (!triggeredCompletionContexts.Any(cc => cc.Items.Count > 0))
{
- var completionContext = await GetContextAsync(
- provider, document, caretPosition, trigger,
- options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
- if (completionContext != null)
- {
- completionContexts.Add(completionContext);
- }
+ return null;
}
- // See if there was a group provided that was exclusive and had items in it. If so, then
+ // All the contexts should be non-empty or have a suggestion item.
+ Debug.Assert(triggeredCompletionContexts.All(HasAnyItems));
+
+ // See if there was a completion context provided that was exclusive. If so, then
// that's all we'll return.
- var firstExclusiveContext = completionContexts.FirstOrDefault(t => t.IsExclusive && t.Items.Any());
+ var firstExclusiveContext = triggeredCompletionContexts.FirstOrDefault(t => t.IsExclusive);
if (firstExclusiveContext != null)
{
@@ -261,41 +267,51 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
isExclusive: true);
}
- // If no exclusive providers provided anything, then go through the remaining
- // triggered list and see if any provide items.
- var nonExclusiveLists = completionContexts.Where(t => !t.IsExclusive).ToList();
+ // Shouldn't be any exclusive completion contexts at this point.
+ Debug.Assert(triggeredCompletionContexts.All(cc => !cc.IsExclusive));
- // If we still don't have any items, then we're definitely done.
- if (!nonExclusiveLists.Any(g => g.Items.Any()))
- {
- return null;
- }
+ // Great! We had some items. Now we want to see if any of the other providers
+ // would like to augment the completion list. For example, we might trigger
+ // enum-completion on space. If enum completion results in any items, then
+ // we'll want to augment the list with all the regular symbol completion items.
+ var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray();
- // If we do have items, then ask all the other (non exclusive providers) if they
- // want to augment the items.
- var usedProviders = nonExclusiveLists.Select(g => g.Provider);
- var nonUsedProviders = providers.Except(usedProviders);
- var nonUsedNonExclusiveLists = new List();
- foreach (var provider in nonUsedProviders)
- {
- var completionList = await GetContextAsync(provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
- if (completionList != null && !completionList.IsExclusive)
- {
- nonUsedNonExclusiveLists.Add(completionList);
- }
- }
+ var augmentingCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
+ document, caretPosition, trigger, options, defaultItemSpan,
+ augmentingProviders, cancellationToken).ConfigureAwait(false);
- var allProvidersAndLists = nonExclusiveLists.Concat(nonUsedNonExclusiveLists).ToList();
- if (allProvidersAndLists.Count == 0)
- {
- return null;
- }
+ var allContexts = triggeredCompletionContexts.Concat(augmentingCompletionContexts);
+ Debug.Assert(allContexts.Length > 0);
// Providers are ordered, but we processed them in our own order. Ensure that the
// groups are properly ordered based on the original providers.
- allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
+ allContexts = allContexts.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
+
+ return MergeAndPruneCompletionLists(allContexts, defaultItemSpan, isExclusive: false);
+ }
+
+ private static bool HasAnyItems(CompletionContext cc)
+ {
+ return cc.Items.Count > 0 || cc.SuggestionModeItem != null;
+ }
+
+ private async Task> ComputeNonEmptyCompletionContextsAsync(
+ Document document, int caretPosition, CompletionTrigger trigger,
+ OptionSet options, TextSpan defaultItemSpan,
+ ImmutableArray providers,
+ CancellationToken cancellationToken)
+ {
+ var completionContextTasks = new List>();
+ foreach (var provider in providers)
+ {
+ completionContextTasks.Add(GetContextAsync(
+ provider, document, caretPosition, trigger,
+ options, defaultItemSpan, cancellationToken));
+ }
- return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan, isExclusive: false);
+ var completionContexts = await Task.WhenAll(completionContextTasks).ConfigureAwait(false);
+ var nonEmptyContexts = completionContexts.Where(HasAnyItems).ToImmutableArray();
+ return nonEmptyContexts;
}
private CompletionList MergeAndPruneCompletionLists(
@@ -520,4 +536,4 @@ int IEqualityComparer>.GetHashCode(ImmutableHashSet GetAnalysisDataAsync(Project project
}
}
- if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
+ if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
// this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
@@ -182,7 +182,7 @@ public async Task GetProjectAnalysisDataAsync(Project
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version);
var builder = new Builder(project.Id, lastResult.Version);
- if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
+ if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
// this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
@@ -301,7 +301,7 @@ private async Task LoadInitialAnalysisDataAsync(Projec
}
}
- if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
+ if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
return new DiagnosticAnalysisResult(project.Id, VersionStamp.Default, ImmutableHashSet.Empty, isEmpty: true, fromBuild: false);
}
@@ -333,7 +333,7 @@ private async Task LoadInitialProjectAnalysisDataAsync
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version);
var builder = new Builder(project.Id, version);
- if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
+ if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
return new DiagnosticAnalysisResult(project.Id, VersionStamp.Default, ImmutableHashSet.Empty, isEmpty: true, fromBuild: false);
}
@@ -355,22 +355,28 @@ private async Task SerializeAsync(DiagnosticDataSerializer serializer, object do
InMemoryStorage.Cache(_owner.Analyzer, ValueTuple.Create(key, stateKey), new CacheEntry(serializer.Version, diagnostics));
}
+ private static Action> s_addSyntaxLocals = (b, id, locals) => b.AddSyntaxLocals(id, locals);
+ private static Action> s_addSemanticLocals = (b, id, locals) => b.AddSemanticLocals(id, locals);
+ private static Action> s_addNonLocals = (b, id, locals) => b.AddNonLocals(id, locals);
+ private static Action> s_addOthers = (b, id, locals) => b.AddOthers(id, locals);
+
private async Task TryDeserializeDocumentAsync(DiagnosticDataSerializer serializer, Document document, Builder builder, CancellationToken cancellationToken)
{
var result = true;
- result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, builder.AddSyntaxLocals, cancellationToken).ConfigureAwait(false);
- result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SemanticStateName, builder.AddSemanticLocals, cancellationToken).ConfigureAwait(false);
- result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.NonLocalStateName, builder.AddNonLocals, cancellationToken).ConfigureAwait(false);
+ result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, s_addSyntaxLocals, builder, cancellationToken).ConfigureAwait(false);
+ result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SemanticStateName, s_addSemanticLocals, builder, cancellationToken).ConfigureAwait(false);
+ result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.NonLocalStateName, s_addNonLocals, builder, cancellationToken).ConfigureAwait(false);
return result;
}
- private async Task TryDeserializeAsync(
+ private async Task TryDeserializeAsync(
DiagnosticDataSerializer serializer,
- object documentOrProject, T key, string stateKey,
- Action> add,
- CancellationToken cancellationToken) where T : class
+ object documentOrProject, TKey key, string stateKey,
+ Action> add,
+ TArg arg,
+ CancellationToken cancellationToken) where TKey : class
{
var diagnostics = await DeserializeAsync(serializer, documentOrProject, key, stateKey, cancellationToken).ConfigureAwait(false);
if (diagnostics == null)
@@ -378,7 +384,7 @@ private async Task TryDeserializeDocumentAsync(DiagnosticDataSerializer se
return false;
}
- add(key, diagnostics.Value);
+ add(arg, key, diagnostics.Value);
return true;
}
diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
index a8554079323e52c0ee11e390e984151221a83f4a..3b0e183ac79f72de0f49f1b617150e43b5d42a25 100644
--- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
+++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
+using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
@@ -61,7 +62,7 @@ protected CodeGenerator(InsertionPoint insertionPoint, SelectionResult selection
protected abstract Task GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(SyntaxAnnotation callsiteAnnotation, CancellationToken cancellationToken);
protected abstract TExpression CreateCallSignature();
- protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, CancellationToken cancellationToken, TExpression initialValue = null);
+ protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, TExpression initialValue, CancellationToken cancellationToken);
protected abstract TStatement CreateAssignmentExpressionStatement(SyntaxToken identifier, TExpression rvalue);
protected abstract TStatement CreateReturnStatement(string identifierName = null);
@@ -188,8 +189,10 @@ protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(Cancellati
// there must be one decl behavior when there is "return value and initialize" variable
Contract.ThrowIfFalse(this.AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken).Single(v => v.ReturnBehavior == ReturnBehavior.Initialization) != null);
- return statements.Concat(
- CreateDeclarationStatement(variable, cancellationToken, CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
+ var declarationStatement = CreateDeclarationStatement(
+ variable, CreateCallSignature(), cancellationToken).WithAdditionalAnnotations(this.CallSiteAnnotation);
+
+ return statements.Concat(declarationStatement);
}
Contract.ThrowIfFalse(variable.ReturnBehavior == ReturnBehavior.Assignment);
@@ -197,19 +200,22 @@ protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(Cancellati
CreateAssignmentExpressionStatement(CreateIdentifier(variable.Name), CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
}
- protected IEnumerable CreateDeclarationStatements(IEnumerable variables, CancellationToken cancellationToken)
+ protected IEnumerable CreateDeclarationStatements(
+ IEnumerable variables, CancellationToken cancellationToken)
{
var list = new List();
foreach (var variable in variables)
{
- list.Add(CreateDeclarationStatement(variable, cancellationToken));
+ list.Add(CreateDeclarationStatement(
+ variable, initialValue: null, cancellationToken: cancellationToken));
}
return list;
}
- protected IEnumerable AddSplitOrMoveDeclarationOutStatementsToCallSite(IEnumerable statements, CancellationToken cancellationToken)
+ protected IEnumerable AddSplitOrMoveDeclarationOutStatementsToCallSite(
+ IEnumerable statements, CancellationToken cancellationToken)
{
var list = new List();
@@ -220,7 +226,8 @@ protected IEnumerable AddSplitOrMoveDeclarationOutStatementsToCallSi
continue;
}
- list.Add(CreateDeclarationStatement(variable, cancellationToken));
+ list.Add(CreateDeclarationStatement(
+ variable, initialValue: null, cancellationToken: cancellationToken));
}
return list;
diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj
index 4a93ce908d0ef3220cb0e9d9eb40e03c179d6071..17dee38125cfeaeff935046b5bacc01984954349 100644
--- a/src/Features/Core/Portable/Features.csproj
+++ b/src/Features/Core/Portable/Features.csproj
@@ -140,9 +140,11 @@
+
+
@@ -306,13 +308,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionItem.DocumentLocationDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.DocumentLocationDefinitionItem.cs
similarity index 96%
rename from src/Features/Core/Portable/FindReferences/DefinitionItem.DocumentLocationDefinitionItem.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionItem.DocumentLocationDefinitionItem.cs
index 87e7c42f5eb1dbc5961f2890ba024172b9e76aea..876bd3ed5bfcb99ff29da2836fbe7d51e590c73e 100644
--- a/src/Features/Core/Portable/FindReferences/DefinitionItem.DocumentLocationDefinitionItem.cs
+++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.DocumentLocationDefinitionItem.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Immutable;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionItem.NonNavigatingDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.NonNavigatingDefinitionItem.cs
similarity index 96%
rename from src/Features/Core/Portable/FindReferences/DefinitionItem.NonNavigatingDefinitionItem.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionItem.NonNavigatingDefinitionItem.cs
index b5966f1298e34c9f717b99b69399222092b0c63f..1dd644c86b5e239f986c9a9e4a894b16565aef75 100644
--- a/src/Features/Core/Portable/FindReferences/DefinitionItem.NonNavigatingDefinitionItem.cs
+++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.NonNavigatingDefinitionItem.cs
@@ -2,7 +2,7 @@
using System.Collections.Immutable;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionItem.SymbolDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.SymbolDefinitionItem.cs
similarity index 98%
rename from src/Features/Core/Portable/FindReferences/DefinitionItem.SymbolDefinitionItem.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionItem.SymbolDefinitionItem.cs
index e9dd74d3c90415b3c2108ee89406dc6b5c1c3a46..48911343ffff2b4fc6cb3c807ec4523674866029 100644
--- a/src/Features/Core/Portable/FindReferences/DefinitionItem.SymbolDefinitionItem.cs
+++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.SymbolDefinitionItem.cs
@@ -8,7 +8,7 @@
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs
similarity index 99%
rename from src/Features/Core/Portable/FindReferences/DefinitionItem.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionItem.cs
index 87b94a12c78f1220f15861be9e108fd093f60a9d..7b11774123a5f1dbd85be7a7379b3fe6544a72d4 100644
--- a/src/Features/Core/Portable/FindReferences/DefinitionItem.cs
+++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs
@@ -4,7 +4,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Completion;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
///
/// Information about a symbol's definition that can be displayed in an editor
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionLocation.DocumentDefinitionLocation.cs b/src/Features/Core/Portable/FindUsages/DefinitionLocation.DocumentDefinitionLocation.cs
similarity index 100%
rename from src/Features/Core/Portable/FindReferences/DefinitionLocation.DocumentDefinitionLocation.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionLocation.DocumentDefinitionLocation.cs
diff --git a/src/Features/Core/Portable/FindReferences/DefinitionsAndReferences.cs b/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs
similarity index 97%
rename from src/Features/Core/Portable/FindReferences/DefinitionsAndReferences.cs
rename to src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs
index f4ad08d3d5c8bbaff1bcaa19d3555d9f1cdacade..8dc54612490a49f33045f791e727976d094fd377 100644
--- a/src/Features/Core/Portable/FindReferences/DefinitionsAndReferences.cs
+++ b/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs
@@ -4,7 +4,7 @@
using System.Collections.Immutable;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
///
/// A collection of s and s
diff --git a/src/Features/Core/Portable/FindReferences/IDefinitionsAndReferencesFactory.cs b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs
similarity index 99%
rename from src/Features/Core/Portable/FindReferences/IDefinitionsAndReferencesFactory.cs
rename to src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs
index 915e9c47ef38c7f9a5bdd64a70819cb523395d9e..80a23337a6acc086618c8b018a38d12cec8bdb60 100644
--- a/src/Features/Core/Portable/FindReferences/IDefinitionsAndReferencesFactory.cs
+++ b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs
@@ -12,7 +12,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
internal interface IDefinitionsAndReferencesFactory : IWorkspaceService
{
diff --git a/src/Features/Core/Portable/FindReferences/SourceReferenceItem.cs b/src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs
similarity index 94%
rename from src/Features/Core/Portable/FindReferences/SourceReferenceItem.cs
rename to src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs
index d16b9f8a3379128a51018366ed2002c476da9513..a9ed3a84fc5763095bc3ec1c61d7b12e409ee44a 100644
--- a/src/Features/Core/Portable/FindReferences/SourceReferenceItem.cs
+++ b/src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs
@@ -2,7 +2,7 @@
using System;
-namespace Microsoft.CodeAnalysis.FindReferences
+namespace Microsoft.CodeAnalysis.FindUsages
{
///
/// Information about a symbol's reference that can be used for diplay and
diff --git a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs
index b88d84c26cadac99fb1558e43ff55e4831c1d257..293ea99f6f970293c501ab422859577a01bfcf2a 100644
--- a/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs
+++ b/src/Features/Core/Portable/IncrementalCaches/SyntaxTreeInfoIncrementalAnalyzerProvider.cs
@@ -20,8 +20,8 @@ private class IncrementalAnalyzer : IncrementalAnalyzerBase
{
public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken)
{
- return SyntaxTreeInfo.PrecalculateAsync(document, cancellationToken);
+ return SyntaxTreeIndex.PrecalculateAsync(document, cancellationToken);
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs
index 3b84603f7afe6e3905792332b1470bdfefee7608..8fb4c1feae1d50f503ce0baf04341910b0a9829e 100644
--- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs
+++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs
@@ -45,7 +45,7 @@ internal abstract partial class AbstractNavigateToSearchService
}
cancellationToken.ThrowIfCancellationRequested();
- var declarationInfo = await document.GetDeclarationInfoAsync(cancellationToken).ConfigureAwait(false);
+ var declarationInfo = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false);
foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos)
{
diff --git a/src/Features/Core/Portable/Shared/TestHooks/FeatureAttribute_Names.cs b/src/Features/Core/Portable/Shared/TestHooks/FeatureAttribute_Names.cs
index 6d87b91cb7e3112ed0e628f3b7b7bcd59c1042e3..1b4ecebf36ce774a8159a7300ea410ec72abc663 100644
--- a/src/Features/Core/Portable/Shared/TestHooks/FeatureAttribute_Names.cs
+++ b/src/Features/Core/Portable/Shared/TestHooks/FeatureAttribute_Names.cs
@@ -9,15 +9,20 @@ internal partial class FeatureAttribute
public const string BraceHighlighting = nameof(BraceHighlighting);
public const string CallHierarchy = nameof(CallHierarchy);
public const string Classification = nameof(Classification);
+ public const string CodeModel = nameof(CodeModel);
public const string CompletionSet = nameof(CompletionSet);
public const string DesignerAttribute = nameof(DesignerAttribute);
- public const string ErrorSquiggles = nameof(ErrorSquiggles);
+ public const string DiagnosticService = nameof(DiagnosticService);
public const string ErrorList = nameof(ErrorList);
- public const string FindReferences = nameof(FindReferences);
- public const string TodoCommentList = nameof(TodoCommentList);
+ public const string ErrorSquiggles = nameof(ErrorSquiggles);
public const string EventHookup = nameof(EventHookup);
+ public const string FindReferences = nameof(FindReferences);
+ public const string GlobalOperation = nameof(GlobalOperation);
+ public const string GoToImplementation = nameof(GoToImplementation);
public const string GraphProvider = nameof(GraphProvider);
+ public const string InfoBar = nameof(InfoBar);
public const string KeywordHighlighting = nameof(KeywordHighlighting);
+ public const string LightBulb = nameof(LightBulb);
public const string LineSeparators = nameof(LineSeparators);
public const string NavigateTo = nameof(NavigateTo);
public const string NavigationBar = nameof(NavigationBar);
@@ -26,15 +31,11 @@ internal partial class FeatureAttribute
public const string ReferenceHighlighting = nameof(ReferenceHighlighting);
public const string Rename = nameof(Rename);
public const string RenameTracking = nameof(RenameTracking);
+ public const string RuleSetEditor = nameof(RuleSetEditor);
public const string SignatureHelp = nameof(SignatureHelp);
public const string Snippets = nameof(Snippets);
public const string SolutionCrawler = nameof(SolutionCrawler);
+ public const string TodoCommentList = nameof(TodoCommentList);
public const string Workspace = nameof(Workspace);
- public const string LightBulb = nameof(LightBulb);
- public const string CodeModel = nameof(CodeModel);
- public const string GlobalOperation = nameof(GlobalOperation);
- public const string DiagnosticService = nameof(DiagnosticService);
- public const string RuleSetEditor = nameof(RuleSetEditor);
- public const string InfoBar = nameof(InfoBar);
}
}
diff --git a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs
index 5e991a182122416fb6032c05e3b2b2726ce68bf3..212d1c63d4f3f37cbfe186a77c1b6f3788c3e168 100644
--- a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs
+++ b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs
@@ -1,6 +1,7 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
@@ -17,27 +18,30 @@
namespace Microsoft.CodeAnalysis.UseCollectionInitializer
{
internal abstract class AbstractUseCollectionInitializerCodeFixProvider<
+ TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
- TVariableDeclarator>
- : CodeFixProvider
+ TVariableDeclaratorSyntax>
+ : SyntaxEditorBasedCodeFixProvider
+ where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
- where TVariableDeclarator : SyntaxNode
+ where TVariableDeclaratorSyntax : SyntaxNode
{
- public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
-
public override ImmutableArray FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId);
+ protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic)
+ => !diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
@@ -46,32 +50,65 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return SpecializedTasks.EmptyTask;
}
- private async Task FixAsync(
- Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
+ protected override Task FixAllAsync(
+ Document document, ImmutableArray diagnostics,
+ SyntaxEditor editor, CancellationToken cancellationToken)
{
- var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
- var objectCreation = (TObjectCreationExpressionSyntax)root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan);
-
+ // Fix-All for this feature is somewhat complicated. As Collection-Initializers
+ // could be arbitrarily nested, we have to make sure that any edits we make
+ // to one Collection-Initializer are seen by any higher ones. In order to do this
+ // we actually process each object-creation-node, one at a time, rewriting
+ // the tree for each node. In order to do this effectively, we use the '.TrackNodes'
+ // feature to keep track of all the object creation nodes as we make edits to
+ // the tree. If we didn't do this, then we wouldn't be able to find the
+ // second object-creation-node after we make the edit for the first one.
+ var workspace = document.Project.Solution.Workspace;
var syntaxFacts = document.GetLanguageService();
- var analyzer = new Analyzer(
- syntaxFacts, objectCreation);
- var matches = analyzer.Analyze();
+ var originalRoot = editor.OriginalRoot;
- var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
+ var originalObjectCreationNodes = new Stack();
+ foreach (var diagnostic in diagnostics)
+ {
+ var objectCreation = (TObjectCreationExpressionSyntax)originalRoot.FindNode(
+ diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
+ originalObjectCreationNodes.Push(objectCreation);
+ }
- var statement = objectCreation.FirstAncestorOrSelf();
- var newStatement = statement.ReplaceNode(
- objectCreation,
- GetNewObjectCreation(objectCreation, matches)).WithAdditionalAnnotations(Formatter.Annotation);
+ // We're going to be continually editing this tree. Track all the nodes we
+ // care about so we can find them across each edit.
+ var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes);
- editor.ReplaceNode(statement, newStatement);
- foreach (var match in matches)
+ while (originalObjectCreationNodes.Count > 0)
{
- editor.RemoveNode(match);
+ var originalObjectCreation = originalObjectCreationNodes.Pop();
+ var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single();
+
+ var analyzer = new ObjectCreationExpressionAnalyzer(
+ syntaxFacts, objectCreation);
+ var matches = analyzer.Analyze();
+ if (matches == null || matches.Value.Length == 0)
+ {
+ continue;
+ }
+
+ var statement = objectCreation.FirstAncestorOrSelf();
+ var newStatement = statement.ReplaceNode(
+ objectCreation,
+ GetNewObjectCreation(objectCreation, matches.Value)).WithAdditionalAnnotations(Formatter.Annotation);
+
+ var subEditor = new SyntaxEditor(currentRoot, workspace);
+
+ subEditor.ReplaceNode(statement, newStatement);
+ foreach (var match in matches)
+ {
+ subEditor.RemoveNode(match);
+ }
+
+ currentRoot = subEditor.GetChangedRoot();
}
- var newRoot = editor.GetChangedRoot();
- return document.WithSyntaxRoot(newRoot);
+ editor.ReplaceNode(originalRoot, currentRoot);
+ return SpecializedTasks.EmptyTask;
}
protected abstract TObjectCreationExpressionSyntax GetNewObjectCreation(
diff --git a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs
index 72639e90af2533c12edb57374be015c27ce37b3e..71a6aab1203675b5d5b8a95c90e1dbbef253c617 100644
--- a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs
+++ b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs
@@ -1,16 +1,23 @@
// 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.Collections;
+using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.UseCollectionInitializer
{
- internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
+ internal abstract partial class AbstractUseCollectionInitializerDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
@@ -18,7 +25,7 @@ internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
- TVariableDeclarator>
+ TVariableDeclaratorSyntax>
: AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
@@ -27,11 +34,11 @@ internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
- where TVariableDeclarator : SyntaxNode
+ where TVariableDeclaratorSyntax : SyntaxNode
{
public bool OpenFileOnly(Workspace workspace) => false;
- protected AbstractUseCollectionInitializerDiagnosticAnalyzer()
+ protected AbstractUseCollectionInitializerDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Simplify_collection_initialization), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Collection_initialization_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
@@ -82,13 +89,10 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
return;
}
- var syntaxFacts = GetSyntaxFactsService();
- var analyzer = new Analyzer<
- TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax,
- TMemberAccessExpressionSyntax, TInvocationExpressionSyntax,
- TExpressionStatementSyntax, TVariableDeclarator>(syntaxFacts, objectCreationExpression);
+ var analyzer = new ObjectCreationExpressionAnalyzer(
+ GetSyntaxFactsService(), objectCreationExpression);
var matches = analyzer.Analyze();
- if (matches.Length == 0)
+ if (matches == null || matches.Value.Length == 0)
{
return;
}
@@ -101,7 +105,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
objectCreationExpression.GetLocation(),
additionalLocations: locations));
- FadeOutCode(context, optionSet, matches, locations);
+ FadeOutCode(context, optionSet, matches.Value, locations);
}
private void FadeOutCode(
@@ -146,256 +150,4 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
protected abstract ISyntaxFactsService GetSyntaxFactsService();
}
-
- internal struct Analyzer<
- TExpressionSyntax,
- TStatementSyntax,
- TObjectCreationExpressionSyntax,
- TMemberAccessExpressionSyntax,
- TInvocationExpressionSyntax,
- TExpressionStatementSyntax,
- TVariableDeclaratorSyntax>
- where TExpressionSyntax : SyntaxNode
- where TStatementSyntax : SyntaxNode
- where TObjectCreationExpressionSyntax : TExpressionSyntax
- where TMemberAccessExpressionSyntax : TExpressionSyntax
- where TInvocationExpressionSyntax : TExpressionSyntax
- where TExpressionStatementSyntax : TStatementSyntax
- where TVariableDeclaratorSyntax : SyntaxNode
- {
- private readonly ISyntaxFactsService _syntaxFacts;
- private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
-
- private TStatementSyntax _containingStatement;
- private SyntaxNodeOrToken _valuePattern;
-
- public Analyzer(
- ISyntaxFactsService syntaxFacts,
- TObjectCreationExpressionSyntax objectCreationExpression) : this()
- {
- _syntaxFacts = syntaxFacts;
- _objectCreationExpression = objectCreationExpression;
- }
-
- internal ImmutableArray Analyze()
- {
- if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
- {
- // Don't bother if this already has an initializer.
- return ImmutableArray.Empty;
- }
-
- _containingStatement = _objectCreationExpression.FirstAncestorOrSelf();
- if (_containingStatement == null)
- {
- return ImmutableArray.Empty;
- }
-
- if (!TryInitializeVariableDeclarationCase() &&
- !TryInitializeAssignmentCase())
- {
- return ImmutableArray.Empty;
- }
-
- var matches = ArrayBuilder.GetInstance();
- AddMatches(matches);
- return matches.ToImmutableAndFree(); ;
- }
-
- private void AddMatches(ArrayBuilder matches)
- {
- var containingBlock = _containingStatement.Parent;
- var foundStatement = false;
-
- var seenInvocation = false;
- var seenIndexAssignment = false;
-
- foreach (var child in containingBlock.ChildNodesAndTokens())
- {
- if (!foundStatement)
- {
- if (child == _containingStatement)
- {
- foundStatement = true;
- }
-
- continue;
- }
-
- if (child.IsToken)
- {
- return;
- }
-
- var statement = child.AsNode() as TExpressionStatementSyntax;
- if (statement == null)
- {
- return;
- }
-
- SyntaxNode instance = null;
- if (!seenIndexAssignment)
- {
- if (TryAnalyzeAddInvocation(statement, out instance))
- {
- seenInvocation = true;
- }
- }
-
- if (!seenInvocation)
- {
- if (TryAnalyzeIndexAssignment(statement, out instance))
- {
- seenIndexAssignment = true;
- }
- }
-
- if (instance == null)
- {
- return;
- }
-
- if (!ValuePatternMatches((TExpressionSyntax)instance))
- {
- return;
- }
-
- matches.Add(statement);
- }
- }
-
- private bool TryAnalyzeIndexAssignment(
- TExpressionStatementSyntax statement,
- out SyntaxNode instance)
- {
- instance = null;
- if (!_syntaxFacts.SupportsIndexingInitializer(statement.SyntaxTree.Options))
- {
- return false;
- }
-
- if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
- {
- return false;
- }
-
- SyntaxNode left, right;
- _syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out right);
-
- if (!_syntaxFacts.IsElementAccessExpression(left))
- {
- return false;
- }
-
- instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left);
- return true;
- }
-
- private bool TryAnalyzeAddInvocation(
- TExpressionStatementSyntax statement,
- out SyntaxNode instance)
- {
- instance = null;
- var invocationExpression = _syntaxFacts.GetExpressionOfExpressionStatement(statement) as TInvocationExpressionSyntax;
- if (invocationExpression == null)
- {
- return false;
- }
-
- var arguments = _syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
- if (arguments.Count < 1)
- {
- return false;
- }
-
- foreach (var argument in arguments)
- {
- if (!_syntaxFacts.IsSimpleArgument(argument))
- {
- return false;
- }
- }
-
- var memberAccess = _syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression) as TMemberAccessExpressionSyntax;
- if (memberAccess == null)
- {
- return false;
- }
-
- if (!_syntaxFacts.IsSimpleMemberAccessExpression(memberAccess))
- {
- return false;
- }
-
- SyntaxNode memberName;
- _syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess, out instance, out memberName);
-
- string name;
- int arity;
- _syntaxFacts.GetNameAndArityOfSimpleName(memberName, out name, out arity);
-
- if (arity != 0 || !name.Equals(nameof(IList.Add)))
- {
- return false;
- }
-
- return true;
- }
-
- private bool ValuePatternMatches(TExpressionSyntax expression)
- {
- if (_valuePattern.IsToken)
- {
- return _syntaxFacts.IsIdentifierName(expression) &&
- _syntaxFacts.AreEquivalent(
- _valuePattern.AsToken(),
- _syntaxFacts.GetIdentifierOfSimpleName(expression));
- }
- else
- {
- return _syntaxFacts.AreEquivalent(
- _valuePattern.AsNode(), expression);
- }
- }
-
- private bool TryInitializeAssignmentCase()
- {
- if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
- {
- return false;
- }
-
- SyntaxNode left, right;
- _syntaxFacts.GetPartsOfAssignmentStatement(_containingStatement, out left, out right);
- if (right != _objectCreationExpression)
- {
- return false;
- }
-
- _valuePattern = left;
- return true;
- }
-
- private bool TryInitializeVariableDeclarationCase()
- {
- if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
- {
- return false;
- }
-
- var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf();
- if (containingDeclarator == null)
- {
- return false;
- }
-
- if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
- {
- return false;
- }
-
- _valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
- return true;
- }
- }
}
\ No newline at end of file
diff --git a/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e7b272b202377aab798bbc8d7c7d2a38edf6d6ee
--- /dev/null
+++ b/src/Features/Core/Portable/UseCollectionInitializer/ObjectCreationExpressionAnalyzer.cs
@@ -0,0 +1,256 @@
+// 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.Collections;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.LanguageServices;
+
+namespace Microsoft.CodeAnalysis.UseCollectionInitializer
+{
+ internal struct ObjectCreationExpressionAnalyzer<
+ TExpressionSyntax,
+ TStatementSyntax,
+ TObjectCreationExpressionSyntax,
+ TMemberAccessExpressionSyntax,
+ TInvocationExpressionSyntax,
+ TExpressionStatementSyntax,
+ TVariableDeclaratorSyntax>
+ where TExpressionSyntax : SyntaxNode
+ where TStatementSyntax : SyntaxNode
+ where TObjectCreationExpressionSyntax : TExpressionSyntax
+ where TMemberAccessExpressionSyntax : TExpressionSyntax
+ where TInvocationExpressionSyntax : TExpressionSyntax
+ where TExpressionStatementSyntax : TStatementSyntax
+ where TVariableDeclaratorSyntax : SyntaxNode
+ {
+ private readonly ISyntaxFactsService _syntaxFacts;
+ private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
+
+ private TStatementSyntax _containingStatement;
+ private SyntaxNodeOrToken _valuePattern;
+
+ public ObjectCreationExpressionAnalyzer(
+ ISyntaxFactsService syntaxFacts,
+ TObjectCreationExpressionSyntax objectCreationExpression) : this()
+ {
+ _syntaxFacts = syntaxFacts;
+ _objectCreationExpression = objectCreationExpression;
+ }
+
+ internal ImmutableArray? Analyze()
+ {
+ if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
+ {
+ // Don't bother if this already has an initializer.
+ return null;
+ }
+
+ _containingStatement = _objectCreationExpression.FirstAncestorOrSelf();
+ if (_containingStatement == null)
+ {
+ return null;
+ }
+
+ if (!TryInitializeVariableDeclarationCase() &&
+ !TryInitializeAssignmentCase())
+ {
+ return null;
+ }
+
+ var matches = ArrayBuilder.GetInstance();
+ AddMatches(matches);
+ return matches.ToImmutableAndFree();
+ }
+
+ private void AddMatches(ArrayBuilder matches)
+ {
+ var containingBlock = _containingStatement.Parent;
+ var foundStatement = false;
+
+ var seenInvocation = false;
+ var seenIndexAssignment = false;
+
+ foreach (var child in containingBlock.ChildNodesAndTokens())
+ {
+ if (!foundStatement)
+ {
+ if (child == _containingStatement)
+ {
+ foundStatement = true;
+ }
+
+ continue;
+ }
+
+ if (child.IsToken)
+ {
+ return;
+ }
+
+ var statement = child.AsNode() as TExpressionStatementSyntax;
+ if (statement == null)
+ {
+ return;
+ }
+
+ SyntaxNode instance = null;
+ if (!seenIndexAssignment)
+ {
+ if (TryAnalyzeAddInvocation(statement, out instance))
+ {
+ seenInvocation = true;
+ }
+ }
+
+ if (!seenInvocation)
+ {
+ if (TryAnalyzeIndexAssignment(statement, out instance))
+ {
+ seenIndexAssignment = true;
+ }
+ }
+
+ if (instance == null)
+ {
+ return;
+ }
+
+ if (!ValuePatternMatches((TExpressionSyntax)instance))
+ {
+ return;
+ }
+
+ matches.Add(statement);
+ }
+ }
+
+ private bool TryAnalyzeIndexAssignment(
+ TExpressionStatementSyntax statement,
+ out SyntaxNode instance)
+ {
+ instance = null;
+ if (!_syntaxFacts.SupportsIndexingInitializer(statement.SyntaxTree.Options))
+ {
+ return false;
+ }
+
+ if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
+ {
+ return false;
+ }
+
+ _syntaxFacts.GetPartsOfAssignmentStatement(statement,
+ out var left, out var right);
+
+ if (!_syntaxFacts.IsElementAccessExpression(left))
+ {
+ return false;
+ }
+
+ instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left);
+ return true;
+ }
+
+ private bool TryAnalyzeAddInvocation(
+ TExpressionStatementSyntax statement,
+ out SyntaxNode instance)
+ {
+ instance = null;
+ var invocationExpression = _syntaxFacts.GetExpressionOfExpressionStatement(statement) as TInvocationExpressionSyntax;
+ if (invocationExpression == null)
+ {
+ return false;
+ }
+
+ var arguments = _syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
+ if (arguments.Count < 1)
+ {
+ return false;
+ }
+
+ foreach (var argument in arguments)
+ {
+ if (!_syntaxFacts.IsSimpleArgument(argument))
+ {
+ return false;
+ }
+ }
+
+ var memberAccess = _syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression) as TMemberAccessExpressionSyntax;
+ if (memberAccess == null)
+ {
+ return false;
+ }
+
+ if (!_syntaxFacts.IsSimpleMemberAccessExpression(memberAccess))
+ {
+ return false;
+ }
+
+ _syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess, out instance, out var memberName);
+ _syntaxFacts.GetNameAndArityOfSimpleName(memberName, out var name, out var arity);
+
+ if (arity != 0 || !name.Equals(nameof(IList.Add)))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool ValuePatternMatches(TExpressionSyntax expression)
+ {
+ if (_valuePattern.IsToken)
+ {
+ return _syntaxFacts.IsIdentifierName(expression) &&
+ _syntaxFacts.AreEquivalent(
+ _valuePattern.AsToken(),
+ _syntaxFacts.GetIdentifierOfSimpleName(expression));
+ }
+ else
+ {
+ return _syntaxFacts.AreEquivalent(
+ _valuePattern.AsNode(), expression);
+ }
+ }
+
+ private bool TryInitializeAssignmentCase()
+ {
+ if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
+ {
+ return false;
+ }
+
+ _syntaxFacts.GetPartsOfAssignmentStatement(_containingStatement,
+ out var left, out var right);
+ if (right != _objectCreationExpression)
+ {
+ return false;
+ }
+
+ _valuePattern = left;
+ return true;
+ }
+
+ private bool TryInitializeVariableDeclarationCase()
+ {
+ if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
+ {
+ return false;
+ }
+
+ var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf();
+ if (containingDeclarator == null)
+ {
+ return false;
+ }
+
+ if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
+ {
+ return false;
+ }
+
+ _valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs
index dc0ae7953d3771304800d4dc6cd3c8f00641fe86..e8963f0aaf221b4d4b5ef2074e0c99eda3060e25 100644
--- a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs
+++ b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs
@@ -1,6 +1,7 @@
// 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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
@@ -17,25 +18,28 @@
namespace Microsoft.CodeAnalysis.UseObjectInitializer
{
internal abstract class AbstractUseObjectInitializerCodeFixProvider<
+ TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TAssignmentStatementSyntax,
- TVariableDeclarator>
- : CodeFixProvider
+ TVariableDeclaratorSyntax>
+ : SyntaxEditorBasedCodeFixProvider
+ where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : TStatementSyntax
- where TVariableDeclarator : SyntaxNode
+ where TVariableDeclaratorSyntax : SyntaxNode
{
- public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
-
public override ImmutableArray FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseObjectInitializerDiagnosticId);
+ protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic)
+ => !diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
@@ -44,37 +48,71 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return SpecializedTasks.EmptyTask;
}
- private async Task FixAsync(
- Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
+ protected override Task FixAllAsync(
+ Document document, ImmutableArray diagnostics,
+ SyntaxEditor editor, CancellationToken cancellationToken)
{
- var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
- var objectCreation = (TObjectCreationExpressionSyntax)root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan);
-
+ // Fix-All for this feature is somewhat complicated. As Object-Initializers
+ // could be arbitrarily nested, we have to make sure that any edits we make
+ // to one Object-Initializer are seen by any higher ones. In order to do this
+ // we actually process each object-creation-node, one at a time, rewriting
+ // the tree for each node. In order to do this effectively, we use the '.TrackNodes'
+ // feature to keep track of all the object creation nodes as we make edits to
+ // the tree. If we didn't do this, then we wouldn't be able to find the
+ // second object-creation-node after we make the edit for the first one.
+ var workspace = document.Project.Solution.Workspace;
var syntaxFacts = document.GetLanguageService();
- var analyzer = new Analyzer(
- syntaxFacts, objectCreation);
- var matches = analyzer.Analyze();
- var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
+ var originalRoot = editor.OriginalRoot;
+ var originalObjectCreationNodes = new Stack();
+ foreach (var diagnostic in diagnostics)
+ {
+ var objectCreation = (TObjectCreationExpressionSyntax)originalRoot.FindNode(
+ diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
+ originalObjectCreationNodes.Push(objectCreation);
+ }
- var statement = objectCreation.FirstAncestorOrSelf();
- var newStatement = statement.ReplaceNode(
- objectCreation,
- GetNewObjectCreation(objectCreation, matches)).WithAdditionalAnnotations(Formatter.Annotation);
+ // We're going to be continually editing this tree. Track all the nodes we
+ // care about so we can find them across each edit.
+ var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes);
- editor.ReplaceNode(statement, newStatement);
- foreach (var match in matches)
+ while (originalObjectCreationNodes.Count > 0)
{
- editor.RemoveNode(match.Statement);
+ var originalObjectCreation = originalObjectCreationNodes.Pop();
+ var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single();
+
+ var analyzer = new ObjectCreationExpressionAnalyzer(
+ syntaxFacts, objectCreation);
+ var matches = analyzer.Analyze();
+
+ if (matches == null || matches.Value.Length == 0)
+ {
+ continue;
+ }
+
+ var statement = objectCreation.FirstAncestorOrSelf();
+ var newStatement = statement.ReplaceNode(
+ objectCreation,
+ GetNewObjectCreation(objectCreation, matches.Value)).WithAdditionalAnnotations(Formatter.Annotation);
+
+ var subEditor = new SyntaxEditor(currentRoot, workspace);
+
+ subEditor.ReplaceNode(statement, newStatement);
+ foreach (var match in matches)
+ {
+ subEditor.RemoveNode(match.Statement);
+ }
+
+ currentRoot = subEditor.GetChangedRoot();
}
- var newRoot = editor.GetChangedRoot();
- return document.WithSyntaxRoot(newRoot);
+ editor.ReplaceNode(editor.OriginalRoot, currentRoot);
+ return SpecializedTasks.EmptyTask;
}
protected abstract TObjectCreationExpressionSyntax GetNewObjectCreation(
TObjectCreationExpressionSyntax objectCreation,
- ImmutableArray> matches);
+ ImmutableArray> matches);
private class MyCodeAction : CodeAction.DocumentChangeAction
{
diff --git a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs
index 0206522973561e241806e8a4165b5f75141c18b4..c79daccaf4ca97d112ca11642792a1458af9d548 100644
--- a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs
+++ b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs
@@ -1,25 +1,22 @@
// 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.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
-using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Options;
-using System;
-using System.Linq;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.UseObjectInitializer
{
- internal abstract class AbstractUseObjectInitializerDiagnosticAnalyzer<
+ internal abstract partial class AbstractUseObjectInitializerDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TAssignmentStatementSyntax,
- TVariableDeclarator>
+ TVariableDeclaratorSyntax>
: AbstractCodeStyleDiagnosticAnalyzer, IBuiltInAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
@@ -27,13 +24,13 @@ internal abstract class AbstractUseObjectInitializerDiagnosticAnalyzer<
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : TStatementSyntax
- where TVariableDeclarator : SyntaxNode
+ where TVariableDeclaratorSyntax : SyntaxNode
{
protected abstract bool FadeOutOperatorToken { get; }
public bool OpenFileOnly(Workspace workspace) => false;
- protected AbstractUseObjectInitializerDiagnosticAnalyzer()
+ protected AbstractUseObjectInitializerDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseObjectInitializerDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Simplify_object_initialization), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Object_initialization_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
@@ -66,11 +63,11 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
}
var syntaxFacts = GetSyntaxFactsService();
- var analyzer = new Analyzer(
- syntaxFacts,
- objectCreationExpression);
- var matches = analyzer.Analyze();
- if (matches.Length == 0)
+ var analyzer = new ObjectCreationExpressionAnalyzer(
+ syntaxFacts, objectCreationExpression);
+ var result = analyzer.Analyze();
+
+ if (result == null || result.Value.Length == 0)
{
return;
}
@@ -83,13 +80,13 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
objectCreationExpression.GetLocation(),
additionalLocations: locations));
- FadeOutCode(context, optionSet, matches, locations);
+ FadeOutCode(context, optionSet, result.Value, locations);
}
private void FadeOutCode(
SyntaxNodeAnalysisContext context,
OptionSet optionSet,
- ImmutableArray> matches,
+ ImmutableArray> matches,
ImmutableArray locations)
{
var syntaxTree = context.Node.SyntaxTree;
@@ -134,280 +131,4 @@ public DiagnosticAnalyzerCategory GetAnalyzerCategory()
return DiagnosticAnalyzerCategory.SemanticDocumentAnalysis;
}
}
-
- internal struct Match
- where TExpressionSyntax : SyntaxNode
- where TMemberAccessExpressionSyntax : TExpressionSyntax
- where TAssignmentStatementSyntax : SyntaxNode
- {
- public readonly TAssignmentStatementSyntax Statement;
- public readonly TMemberAccessExpressionSyntax MemberAccessExpression;
- public readonly TExpressionSyntax Initializer;
-
- public Match(
- TAssignmentStatementSyntax statement,
- TMemberAccessExpressionSyntax memberAccessExpression,
- TExpressionSyntax initializer)
- {
- Statement = statement;
- MemberAccessExpression = memberAccessExpression;
- Initializer = initializer;
- }
- }
-
- internal struct Analyzer<
- TExpressionSyntax,
- TStatementSyntax,
- TObjectCreationExpressionSyntax,
- TMemberAccessExpressionSyntax,
- TAssignmentStatementSyntax,
- TVariableDeclaratorSyntax>
- where TExpressionSyntax : SyntaxNode
- where TStatementSyntax : SyntaxNode
- where TObjectCreationExpressionSyntax : TExpressionSyntax
- where TMemberAccessExpressionSyntax : TExpressionSyntax
- where TAssignmentStatementSyntax : TStatementSyntax
- where TVariableDeclaratorSyntax : SyntaxNode
- {
- private readonly ISyntaxFactsService _syntaxFacts;
- private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
-
- private TStatementSyntax _containingStatement;
- private SyntaxNodeOrToken _valuePattern;
-
- public Analyzer(
- ISyntaxFactsService syntaxFacts,
- TObjectCreationExpressionSyntax objectCreationExpression) : this()
- {
- _syntaxFacts = syntaxFacts;
- _objectCreationExpression = objectCreationExpression;
- }
-
- internal ImmutableArray> Analyze()
- {
- if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
- {
- // Don't bother if this already has an initializer.
- return ImmutableArray>.Empty;
- }
-
- _containingStatement = _objectCreationExpression.FirstAncestorOrSelf();
- if (_containingStatement == null)
- {
- return ImmutableArray>.Empty;
- }
-
- if (!TryInitializeVariableDeclarationCase() &&
- !TryInitializeAssignmentCase())
- {
- return ImmutableArray>.Empty;
- }
-
- var containingBlock = _containingStatement.Parent;
- var foundStatement = false;
-
- var matches = ArrayBuilder>.GetInstance();
- HashSet seenNames = null;
-
- foreach (var child in containingBlock.ChildNodesAndTokens())
- {
- if (!foundStatement)
- {
- if (child == _containingStatement)
- {
- foundStatement = true;
- }
-
- continue;
- }
-
- if (child.IsToken)
- {
- break;
- }
-
- var statement = child.AsNode() as TAssignmentStatementSyntax;
- if (statement == null)
- {
- break;
- }
-
- if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
- {
- break;
- }
-
- _syntaxFacts.GetPartsOfAssignmentStatement(
- statement, out var left, out var right);
-
- var rightExpression = right as TExpressionSyntax;
- var leftMemberAccess = left as TMemberAccessExpressionSyntax;
-
- if (!_syntaxFacts.IsSimpleMemberAccessExpression(leftMemberAccess))
- {
- break;
- }
-
- var expression = (TExpressionSyntax)_syntaxFacts.GetExpressionOfMemberAccessExpression(leftMemberAccess);
- if (!ValuePatternMatches(expression))
- {
- break;
- }
-
- // Don't offer this fix if the value we're initializing is itself referenced
- // on the RHS of the assignment. For example:
- //
- // var v = new X();
- // v.Prop = v.Prop.WithSomething();
- //
- // Or with
- //
- // v = new X();
- // v.Prop = v.Prop.WithSomething();
- //
- // In the first case, 'v' is being initialized, and so will not be available
- // in the object initializer we create.
- //
- // In the second case we'd change semantics because we'd access the old value
- // before the new value got written.
- if (ExpressionContainsValuePattern(rightExpression))
- {
- break;
- }
-
- // If we have code like "x.v = .Length.ToString()"
- // then we don't want to change this into:
- //
- // var x = new Whatever() With { .v = .Length.ToString() }
- //
- // The problem here is that .Length will change it's meaning to now refer to the
- // object that we're creating in our object-creation expression.
- if (ImplicitMemberAccessWouldBeAffected(rightExpression))
- {
- break;
- }
-
- // found a match!
- seenNames = seenNames ?? new HashSet();
-
- // If we see an assignment to the same property/field, we can't convert it
- // to an initializer.
- var name = _syntaxFacts.GetNameOfMemberAccessExpression(leftMemberAccess);
- var identifier = _syntaxFacts.GetIdentifierOfSimpleName(name);
- if (!seenNames.Add(identifier.ValueText))
- {
- break;
- }
-
- matches.Add(new Match(
- statement, leftMemberAccess, rightExpression));
- }
-
- return matches.ToImmutableAndFree();
- }
-
- private bool ImplicitMemberAccessWouldBeAffected(SyntaxNode node)
- {
- if (node != null)
- {
- foreach (var child in node.ChildNodesAndTokens())
- {
- if (child.IsNode)
- {
- if (ImplicitMemberAccessWouldBeAffected(child.AsNode()))
- {
- return true;
- }
- }
- }
-
- if (_syntaxFacts.IsSimpleMemberAccessExpression(node))
- {
- var expression = _syntaxFacts.GetExpressionOfMemberAccessExpression(
- node, allowImplicitTarget: true);
-
- // If we're implicitly referencing some target that is before the
- // object creation expression, then our semantics will change.
- if (expression != null && expression.SpanStart < _objectCreationExpression.SpanStart)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- private bool ExpressionContainsValuePattern(TExpressionSyntax expression)
- {
- foreach (var subExpression in expression.DescendantNodesAndSelf().OfType())
- {
- if (!_syntaxFacts.IsNameOfMemberAccessExpression(subExpression))
- {
- if (ValuePatternMatches(subExpression))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- private bool ValuePatternMatches(TExpressionSyntax expression)
- {
- if (_valuePattern.IsToken)
- {
- return _syntaxFacts.IsIdentifierName(expression) &&
- _syntaxFacts.AreEquivalent(
- _valuePattern.AsToken(),
- _syntaxFacts.GetIdentifierOfSimpleName(expression));
- }
- else
- {
- return _syntaxFacts.AreEquivalent(
- _valuePattern.AsNode(), expression);
- }
- }
-
- private bool TryInitializeAssignmentCase()
- {
- if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
- {
- return false;
- }
-
- _syntaxFacts.GetPartsOfAssignmentStatement(
- _containingStatement, out var left, out var right);
- if (right != _objectCreationExpression)
- {
- return false;
- }
-
- _valuePattern = left;
- return true;
- }
-
- private bool TryInitializeVariableDeclarationCase()
- {
- if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
- {
- return false;
- }
-
- var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf();
- if (containingDeclarator == null)
- {
- return false;
- }
-
- if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
- {
- return false;
- }
-
- _valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
- return true;
- }
- }
}
\ No newline at end of file
diff --git a/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs b/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..406790df6b9ca61e9b22d2d2c40c388b4ee5aaf2
--- /dev/null
+++ b/src/Features/Core/Portable/UseObjectInitializer/ObjectCreationExpressionAnalyzer.cs
@@ -0,0 +1,291 @@
+// 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.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis.LanguageServices;
+
+namespace Microsoft.CodeAnalysis.UseObjectInitializer
+{
+ internal struct ObjectCreationExpressionAnalyzer<
+ TExpressionSyntax,
+ TStatementSyntax,
+ TObjectCreationExpressionSyntax,
+ TMemberAccessExpressionSyntax,
+ TAssignmentStatementSyntax,
+ TVariableDeclaratorSyntax>
+ where TExpressionSyntax : SyntaxNode
+ where TStatementSyntax : SyntaxNode
+ where TObjectCreationExpressionSyntax : TExpressionSyntax
+ where TMemberAccessExpressionSyntax : TExpressionSyntax
+ where TAssignmentStatementSyntax : TStatementSyntax
+ where TVariableDeclaratorSyntax : SyntaxNode
+ {
+ private readonly ISyntaxFactsService _syntaxFacts;
+ private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
+
+ private TStatementSyntax _containingStatement;
+ private SyntaxNodeOrToken _valuePattern;
+
+ public ObjectCreationExpressionAnalyzer(
+ ISyntaxFactsService syntaxFacts,
+ TObjectCreationExpressionSyntax objectCreationExpression) : this()
+ {
+ _syntaxFacts = syntaxFacts;
+ _objectCreationExpression = objectCreationExpression;
+ }
+
+ internal ImmutableArray>? Analyze()
+ {
+ if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
+ {
+ // Don't bother if this already has an initializer.
+ return null;
+ }
+
+ _containingStatement = _objectCreationExpression.FirstAncestorOrSelf();
+ if (_containingStatement == null)
+ {
+ return null;
+ }
+
+ if (!TryInitializeVariableDeclarationCase() &&
+ !TryInitializeAssignmentCase())
+ {
+ return null;
+ }
+
+ var containingBlock = _containingStatement.Parent;
+ var foundStatement = false;
+
+ var matches = ArrayBuilder>.GetInstance();
+ HashSet seenNames = null;
+
+ foreach (var child in containingBlock.ChildNodesAndTokens())
+ {
+ if (!foundStatement)
+ {
+ if (child == _containingStatement)
+ {
+ foundStatement = true;
+ continue;
+ }
+
+ continue;
+ }
+
+ if (child.IsToken)
+ {
+ break;
+ }
+
+ var statement = child.AsNode() as TAssignmentStatementSyntax;
+ if (statement == null)
+ {
+ break;
+ }
+
+ if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
+ {
+ break;
+ }
+
+ _syntaxFacts.GetPartsOfAssignmentStatement(
+ statement, out var left, out var right);
+
+ var rightExpression = right as TExpressionSyntax;
+ var leftMemberAccess = left as TMemberAccessExpressionSyntax;
+
+ if (!_syntaxFacts.IsSimpleMemberAccessExpression(leftMemberAccess))
+ {
+ break;
+ }
+
+ var expression = (TExpressionSyntax)_syntaxFacts.GetExpressionOfMemberAccessExpression(leftMemberAccess);
+ if (!ValuePatternMatches(expression))
+ {
+ break;
+ }
+
+ // Don't offer this fix if the value we're initializing is itself referenced
+ // on the RHS of the assignment. For example:
+ //
+ // var v = new X();
+ // v.Prop = v.Prop.WithSomething();
+ //
+ // Or with
+ //
+ // v = new X();
+ // v.Prop = v.Prop.WithSomething();
+ //
+ // In the first case, 'v' is being initialized, and so will not be available
+ // in the object initializer we create.
+ //
+ // In the second case we'd change semantics because we'd access the old value
+ // before the new value got written.
+ if (ExpressionContainsValuePattern(rightExpression))
+ {
+ break;
+ }
+
+ // If we have code like "x.v = .Length.ToString()"
+ // then we don't want to change this into:
+ //
+ // var x = new Whatever() With { .v = .Length.ToString() }
+ //
+ // The problem here is that .Length will change it's meaning to now refer to the
+ // object that we're creating in our object-creation expression.
+ if (ImplicitMemberAccessWouldBeAffected(rightExpression))
+ {
+ break;
+ }
+
+ // found a match!
+ seenNames = seenNames ?? new HashSet();
+
+ // If we see an assignment to the same property/field, we can't convert it
+ // to an initializer.
+ var name = _syntaxFacts.GetNameOfMemberAccessExpression(leftMemberAccess);
+ var identifier = _syntaxFacts.GetIdentifierOfSimpleName(name);
+ if (!seenNames.Add(identifier.ValueText))
+ {
+ break;
+ }
+
+ matches.Add(new Match(
+ statement, leftMemberAccess, rightExpression));
+ }
+
+ return matches.ToImmutableAndFree();
+ }
+
+ private bool ImplicitMemberAccessWouldBeAffected(SyntaxNode node)
+ {
+ if (node != null)
+ {
+ foreach (var child in node.ChildNodesAndTokens())
+ {
+ if (child.IsNode)
+ {
+ if (ImplicitMemberAccessWouldBeAffected(child.AsNode()))
+ {
+ return true;
+ }
+ }
+ }
+
+ if (_syntaxFacts.IsSimpleMemberAccessExpression(node))
+ {
+ var expression = _syntaxFacts.GetExpressionOfMemberAccessExpression(
+ node, allowImplicitTarget: true);
+
+ // If we're implicitly referencing some target that is before the
+ // object creation expression, then our semantics will change.
+ if (expression != null && expression.SpanStart < _objectCreationExpression.SpanStart)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool ExpressionContainsValuePattern(TExpressionSyntax expression)
+ {
+ foreach (var subExpression in expression.DescendantNodesAndSelf().OfType())
+ {
+ if (!_syntaxFacts.IsNameOfMemberAccessExpression(subExpression))
+ {
+ if (ValuePatternMatches(subExpression))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool ValuePatternMatches(TExpressionSyntax expression)
+ {
+ if (_valuePattern.IsToken)
+ {
+ return _syntaxFacts.IsIdentifierName(expression) &&
+ _syntaxFacts.AreEquivalent(
+ _valuePattern.AsToken(),
+ _syntaxFacts.GetIdentifierOfSimpleName(expression));
+ }
+ else
+ {
+ return _syntaxFacts.AreEquivalent(
+ _valuePattern.AsNode(), expression);
+ }
+ }
+
+ private bool TryInitializeAssignmentCase()
+ {
+ if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
+ {
+ return false;
+ }
+
+ _syntaxFacts.GetPartsOfAssignmentStatement(
+ _containingStatement, out var left, out var right);
+ if (right != _objectCreationExpression)
+ {
+ return false;
+ }
+
+ _valuePattern = left;
+ return true;
+ }
+
+ private bool TryInitializeVariableDeclarationCase()
+ {
+ if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
+ {
+ return false;
+ }
+
+ var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf();
+ if (containingDeclarator == null)
+ {
+ return false;
+ }
+
+ if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
+ {
+ return false;
+ }
+
+ _valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
+ return true;
+ }
+ }
+
+ internal struct Match<
+ TExpressionSyntax,
+ TStatementSyntax,
+ TMemberAccessExpressionSyntax,
+ TAssignmentStatementSyntax>
+ where TExpressionSyntax : SyntaxNode
+ where TStatementSyntax : SyntaxNode
+ where TMemberAccessExpressionSyntax : TExpressionSyntax
+ where TAssignmentStatementSyntax : TStatementSyntax
+ {
+ public readonly TAssignmentStatementSyntax Statement;
+ public readonly TMemberAccessExpressionSyntax MemberAccessExpression;
+ public readonly TExpressionSyntax Initializer;
+
+ public Match(
+ TAssignmentStatementSyntax statement,
+ TMemberAccessExpressionSyntax memberAccessExpression,
+ TExpressionSyntax initializer)
+ {
+ Statement = statement;
+ MemberAccessExpression = memberAccessExpression;
+ Initializer = initializer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.vb b/src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.vb
index 1b263144dcb0f7beede3eb5de5ed7a039498ac17..32f45dc5d4fc40de5dddbf6c85f1b65f9d3a1844 100644
--- a/src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.vb
+++ b/src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.vb
@@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.Simplification
+Imports Microsoft.CodeAnalysis.Options
Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Partial Friend Class VisualBasicMethodExtractor
@@ -349,9 +350,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return identifier.CreateAssignmentExpressionStatementWithValue(rvalue)
End Function
- Protected Overrides Function CreateDeclarationStatement(variable As VariableInfo,
- cancellationToken As CancellationToken,
- Optional givenInitializer As ExpressionSyntax = Nothing) As StatementSyntax
+ Protected Overrides Function CreateDeclarationStatement(
+ variable As VariableInfo,
+ givenInitializer As ExpressionSyntax,
+ cancellationToken As CancellationToken) As StatementSyntax
Dim shouldInitializeWithNothing = (variable.GetDeclarationBehavior(cancellationToken) = DeclarationBehavior.MoveOut OrElse variable.GetDeclarationBehavior(cancellationToken) = DeclarationBehavior.SplitOut) AndAlso
(variable.ParameterModifier = ParameterBehavior.Out)
diff --git a/src/Features/VisualBasic/Portable/UseCollectionInitializer/VisualBasicUseCollectionInitializerCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseCollectionInitializer/VisualBasicUseCollectionInitializerCodeFixProvider.vb
index a231cd9c3862abbda67b668134b3d8b19990623b..de18e1c6b8e9f51c5f689a8fd87e5798def3d58a 100644
--- a/src/Features/VisualBasic/Portable/UseCollectionInitializer/VisualBasicUseCollectionInitializerCodeFixProvider.vb
+++ b/src/Features/VisualBasic/Portable/UseCollectionInitializer/VisualBasicUseCollectionInitializerCodeFixProvider.vb
@@ -10,6 +10,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer
Friend Class VisualBasicUseCollectionInitializerCodeFixProvider
Inherits AbstractUseCollectionInitializerCodeFixProvider(Of
+ SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
diff --git a/src/Features/VisualBasic/Portable/UseObjectInitializer/VisualBasicUseObjectInitializerCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseObjectInitializer/VisualBasicUseObjectInitializerCodeFixProvider.vb
index 8972658051ae56ff87b1a5bf60cc01f7a0274c19..bdbf4fbee3f78537e913f5f7259617024d0a75f5 100644
--- a/src/Features/VisualBasic/Portable/UseObjectInitializer/VisualBasicUseObjectInitializerCodeFixProvider.vb
+++ b/src/Features/VisualBasic/Portable/UseObjectInitializer/VisualBasicUseObjectInitializerCodeFixProvider.vb
@@ -10,6 +10,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
Friend Class VisualBasicUseObjectInitializerCodeFixProvider
Inherits AbstractUseObjectInitializerCodeFixProvider(Of
+ SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
@@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
Protected Overrides Function GetNewObjectCreation(
objectCreation As ObjectCreationExpressionSyntax,
- matches As ImmutableArray(Of Match(Of AssignmentStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax))) As ObjectCreationExpressionSyntax
+ matches As ImmutableArray(Of Match(Of ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, AssignmentStatementSyntax))) As ObjectCreationExpressionSyntax
Dim initializer = SyntaxFactory.ObjectMemberInitializer(
CreateFieldInitializers(matches))
@@ -30,7 +31,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
End Function
Private Function CreateFieldInitializers(
- matches As ImmutableArray(Of Match(Of AssignmentStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax))) As SeparatedSyntaxList(Of FieldInitializerSyntax)
+ matches As ImmutableArray(Of Match(Of ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, AssignmentStatementSyntax))) As SeparatedSyntaxList(Of FieldInitializerSyntax)
Dim nodesAndTokens = New List(Of SyntaxNodeOrToken)
For i = 0 To matches.Length - 1
diff --git a/src/NuGet/Microsoft.CodeAnalysis.Common.nuspec b/src/NuGet/Microsoft.CodeAnalysis.Common.nuspec
index eac10fcd6f28fe76904c72ee36cdb9310b10f5d5..596e7f707d23dfca4e4d42e8dee2bf48f620ca40 100644
--- a/src/NuGet/Microsoft.CodeAnalysis.Common.nuspec
+++ b/src/NuGet/Microsoft.CodeAnalysis.Common.nuspec
@@ -43,6 +43,7 @@
+
diff --git a/src/NuGet/Microsoft.Net.Compilers.nuspec b/src/NuGet/Microsoft.Net.Compilers.nuspec
index 8a125e3043035cc7a0b7f81c3cacbed12eaf8233..2c5342e3563941815c6e5493d02d3e0588c5dabd 100644
--- a/src/NuGet/Microsoft.Net.Compilers.nuspec
+++ b/src/NuGet/Microsoft.Net.Compilers.nuspec
@@ -70,9 +70,9 @@ Supported Platforms:
+
-
diff --git a/src/Tools/Github/GitMergeBot/GitHubRepository.cs b/src/Tools/Github/GitMergeBot/GitHubRepository.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c0fd18fa86054db23cb652d69b0d57ad2f67845a
--- /dev/null
+++ b/src/Tools/Github/GitMergeBot/GitHubRepository.cs
@@ -0,0 +1,124 @@
+// 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.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Octokit;
+
+namespace GitMergeBot
+{
+ internal sealed class GitHubRepository : RepositoryBase
+ {
+ private GitHubClient _client;
+
+ public GitHubRepository(string path, string repoName, string userName, string authToken)
+ : base(path, repoName, userName, authToken)
+ {
+ _client = new GitHubClient(new ProductHeaderValue(userName))
+ {
+ Credentials = new Credentials(authToken)
+ };
+ }
+
+ public override async Task ShouldMakePullRequestAsync(string title)
+ {
+ return (await GetExistingMergePrsAsync(title)).Count == 0;
+ }
+
+ public override async Task CreatePullRequestAsync(string title, string destinationOwner, string pullRequestBranch, string prBranchSourceRemote, string sourceBranch, string destinationBranch)
+ {
+ var remoteName = $"{UserName}-{RepositoryName}";
+ var prMessage = $@"
+This is an automatically generated pull request from {sourceBranch} into {destinationBranch}.
+
+@dotnet/roslyn-infrastructure:
+
+``` bash
+git remote add {remoteName} ""https://github.com/{UserName}/{RepositoryName}.git""
+git fetch {remoteName}
+git fetch {prBranchSourceRemote}
+git checkout {pullRequestBranch}
+git reset --hard {prBranchSourceRemote}/{destinationBranch}
+git merge {prBranchSourceRemote}/{sourceBranch}
+# Fix merge conflicts
+git commit
+git push {remoteName} {pullRequestBranch} --force
+```
+
+Once all conflicts are resolved and all the tests pass, you are free to merge the pull request.
+".Trim();
+
+ try
+ {
+ var pullRequest = await _client.PullRequest.Create(
+ owner: destinationOwner,
+ name: RepositoryName,
+ newPullRequest: new NewPullRequest(
+ title: title,
+ head: $"{UserName}:{pullRequestBranch}",
+ baseRef: destinationBranch)
+ {
+ Body = prMessage
+ }
+ );
+
+ // The reason for this delay is twofold:
+ //
+ // * Github has a bug in which it can "create" a pull request without that pull request
+ // being able to be commented on for a short period of time.
+ // * The Jenkins "comment watcher" has a bug whereby any comment posted shortly after
+ // pull-request creation is ignored.
+ //
+ // Thus, this delay sidesteps both of those bugs by asking for a VSI test 30 seconds after
+ // the creation of the PR. Ugly, yes; but the only *real* way to sidestep this would be to
+ // 1) Fix github, 2) Fix jenkins, and while those might be lofty goals, they are not in the
+ // scope of this PR.
+ await Task.Delay(TimeSpan.FromSeconds(30.0));
+
+ await _client.Issue.Comment.Create(destinationOwner, RepositoryName, pullRequest.Number, "@dotnet-bot test vsi please");
+ }
+ catch (Exception ex) when (DidPullRequestFailDueToNoChanges(ex))
+ {
+ Console.WriteLine("There were no commits between the specified branchs. Pull request not created.");
+ }
+ }
+
+ /// The existing open merge PRs.
+ private async Task> GetExistingMergePrsAsync(string newBranchPrefix)
+ {
+ var allPullRequests = await _client.PullRequest.GetAllForRepository(UserName, RepositoryName);
+ var openPrs = allPullRequests.Where(pr => pr.Head.Ref.StartsWith(newBranchPrefix) && pr.User.Login == UserName).ToList();
+
+ Console.WriteLine($"Found {openPrs.Count} existing open merge pull requests.");
+ foreach (var pr in openPrs)
+ {
+ Console.WriteLine($" Open PR: {pr.HtmlUrl}");
+ }
+
+ return openPrs;
+ }
+
+ ///
+ /// The Octokit API fails on pull request creation if the PR would have been empty, but there is no way
+ /// to know that ahead of time.
+ ///
+ /// The fall-back is to check for a very specific "failure".
+ ///
+ private static bool DidPullRequestFailDueToNoChanges(Exception ex)
+ {
+ if (!(ex is ApiValidationException apiException))
+ {
+ return false;
+ }
+
+ if (apiException.ApiError.Errors.Count != 1)
+ {
+ return false;
+ }
+
+ var error = apiException.ApiError.Errors.Single();
+ return error.Message.StartsWith("No commits between");
+ }
+ }
+}
diff --git a/src/Tools/Github/GitMergeBot/GitMergeBot.csproj b/src/Tools/Github/GitMergeBot/GitMergeBot.csproj
index c28180f3eb82101eeacc64abc57ddd1c0af4e18a..9120455213f44d93687084349e83de9c2cf0280b 100644
--- a/src/Tools/Github/GitMergeBot/GitMergeBot.csproj
+++ b/src/Tools/Github/GitMergeBot/GitMergeBot.csproj
@@ -1,5 +1,6 @@
+
Debug
@@ -11,6 +12,8 @@
GitMergeBot
v4.6
true
+
+
AnyCPU
@@ -32,12 +35,24 @@
4
+
+ ..\..\..\..\..\packages\LibGit2Sharp.0.22.0\lib\net40\LibGit2Sharp.dll
+ True
+
+
+ ..\..\..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+ True
+
..\..\..\..\..\packages\Octokit.0.17.0\lib\net45\Octokit.dll
True
+
+ ..\..\..\..\..\packages\System.ValueTuple.4.3.0-preview1-24530-04\lib\netstandard1.0\System.ValueTuple.dll
+ True
+
@@ -46,16 +61,26 @@
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
-
+
\ No newline at end of file
diff --git a/src/Tools/Github/GitMergeBot/Options.cs b/src/Tools/Github/GitMergeBot/Options.cs
index fce4100e20768280078e2cd3be108e819cbb5f12..e380fa577759675d28f8cd54b677949ed71f671a 100644
--- a/src/Tools/Github/GitMergeBot/Options.cs
+++ b/src/Tools/Github/GitMergeBot/Options.cs
@@ -6,16 +6,88 @@ namespace GitMergeBot
{
internal sealed class Options
{
- public string AuthToken { get; set; }
- public string RepoName { get; set; }
- public string SourceBranch { get; set; }
- public string DestinationBranch { get; set; }
- public string SourceUser { get; set; }
- public string DestinationUser { get; set; }
public bool Force { get; set; }
public bool Debug { get; set; }
- public bool ShowHelp { get; set; }
+ public string RepositoryPath { get; set; }
+ public RepositoryType SourceRepoType { get; set; }
+ public string SourceRepoName { get; set; }
+ public string SourceProject { get; set; }
+ public string SourceUserId { get; set; }
+ public string SourceUserName { get; set; }
+ public string SourcePassword { get; set; }
+ public string SourceRemoteName { get; set; }
+ public string SourceBranchName { get; set; }
- public bool AreValid => new[] { AuthToken, RepoName, SourceBranch, DestinationBranch, SourceUser, DestinationUser }.All(s => s != null);
+ public bool PushBranchToDestination { get; set; }
+
+ private string _prBranchSourceRemote;
+ private RepositoryType? _destinationRepoType;
+ private string _destinationRepoOwner;
+ private string _destinationRepoName;
+ private string _destinationUserName;
+ private string _destinationPassword;
+ private string _destinationRemoteName;
+ private string _destinationBranchName;
+
+ public string DestinationProject { get; set; }
+ public string DestinationUserId { get; set; }
+
+ public string PullRequestBranchSourceRemote
+ {
+ get { return _prBranchSourceRemote ?? SourceRemoteName; }
+ set { _prBranchSourceRemote = value; }
+ }
+
+ public RepositoryType DestinationRepoType
+ {
+ get { return _destinationRepoType ?? SourceRepoType; }
+ set { _destinationRepoType = value; }
+ }
+
+ public string DestinationRepoOwner
+ {
+ get { return _destinationRepoOwner ?? DestinationUserName; }
+ set { _destinationRepoOwner = value; }
+ }
+
+ public string DestinationRepoName
+ {
+ get { return _destinationRepoName ?? SourceRepoName; }
+ set { _destinationRepoName = value; }
+ }
+
+ public string DestinationUserName
+ {
+ get { return _destinationUserName ?? SourceUserName; }
+ set { _destinationUserName = value; }
+ }
+
+ public string DestinationPassword
+ {
+ get { return _destinationPassword ?? SourcePassword; }
+ set { _destinationPassword = value; }
+ }
+
+ public string DestinationRemoteName
+ {
+ get { return _destinationRemoteName ?? SourceRemoteName; }
+ set { _destinationRemoteName = value; }
+ }
+
+ public string DestinationBranchName
+ {
+ get { return _destinationBranchName ?? SourceBranchName; }
+ set { _destinationBranchName = value; }
+ }
+
+ public bool IsValid => new[]
+ {
+ RepositoryPath,
+ SourceRepoName,
+ SourceUserName,
+ SourcePassword,
+ SourceRemoteName,
+ SourceBranchName
+ }.All(s => s != null);
}
}
diff --git a/src/Tools/Github/GitMergeBot/Program.cs b/src/Tools/Github/GitMergeBot/Program.cs
index 62f79a56dd97052a89b3f9b96b228da4d671d78f..62972265fb9910567f972d6802fab70ffad16166 100644
--- a/src/Tools/Github/GitMergeBot/Program.cs
+++ b/src/Tools/Github/GitMergeBot/Program.cs
@@ -1,215 +1,152 @@
// 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.Generic;
-using System.Linq;
-using System.Net;
using System.Reflection;
using System.Threading.Tasks;
+using LibGit2Sharp;
using Mono.Options;
-using Octokit;
+
+using static System.Console;
namespace GitMergeBot
{
- class Program
+ internal sealed class Program
{
static int Main(string[] args)
{
var exeName = Assembly.GetExecutingAssembly().GetName().Name;
+ var showHelp = false;
var options = new Options();
-
- // default to using an environment variable, but allow an explicitly provided value to override
- options.AuthToken = Environment.GetEnvironmentVariable("AUTH_CODE");
var parameters = new OptionSet()
{
$"Usage: {exeName} [options]",
"Create a pull request from the specified user and branch to another specified user and branch.",
"",
"Options:",
- { "a|auth=", "The GitHub authentication token.", value => options.AuthToken = value },
- { "r|repo=", "The name of the remote repository.", value => options.RepoName = value },
- { "s|source=", "The source branch of the merge operation.", value => options.SourceBranch = value },
- { "d|dest=", "The destination branch of the merge operation.", value => options.DestinationBranch = value },
- { "su|sourceuser=", "The user hosting the source branch of the merge operation.", value => options.SourceUser = value },
- { "du|destuser=", "The user hosting the destination branch of the merge operation.", value => options.DestinationUser = value },
+ { "repopath=", "The local path to the repository.", value => options.RepositoryPath = value },
+ { "sourcetype=", "The source repository type. Valid values are 'GitHub' and 'VisualStudioOnline'.", value => options.SourceRepoType = (RepositoryType)Enum.Parse(typeof(RepositoryType), value) },
+ { "sourcereponame=", "The name of the source repository.", value => options.SourceRepoName = value },
+ { "sourceproject=", "The name of the source project. Only needed for VisualStudioOnline repos.", value => options.SourceProject = value },
+ { "sourceuserid=", "The source user ID. Only needed for VisualStudioOnline repos.", value => options.SourceUserId = value },
+ { "sourceuser=", "The source user name.", value => options.SourceUserName = value },
+ { "sourcepassword=", "The source password.", value => options.SourcePassword = value },
+ { "sourceremote=", "The source remote name.", value => options.SourceRemoteName = value },
+ { "sourcebranch=", "The source branch name.", value => options.SourceBranchName = value },
+ { "pushtodestination=", "If true the PR branch will be pushed to the destination repository; if false the PR branch will be pushed to the source.", value => options.PushBranchToDestination = value != null },
+ { "prbranchsourceremote=", "The name of the remote the PR should initiate from. Defaults to `sourceremote` parameter.", value => options.PullRequestBranchSourceRemote = value },
+ { "destinationtype=", "The destination repository type. Valid values are 'GitHub' and 'VisualStudioOnline'. Defaults to `sourcetype` parameter.", value => options.DestinationRepoType = (RepositoryType)Enum.Parse(typeof(RepositoryType), value) },
+ { "destinationrepoowner=", "", value => options.DestinationRepoOwner = value },
+ { "destinationreponame=", "The name of the destination repository. Defaults to `sourcereponame` parameter.", value => options.DestinationRepoName = value },
+ { "destinationproject=", "The name of the destination project. Only needed for VisualStudioOnline repos.", value => options.DestinationProject = value },
+ { "destinationuserid=", "The destination user ID. Only needed for VisualStudioOnline repos.", value => options.DestinationUserId = value },
+ { "destinationuser=", "The destination user name. Defaults to `sourceuser` parameter.", value => options.DestinationUserName = value },
+ { "destinationpassword=", "The destination password. Defaults to `sourcepassword` parameter.", value => options.DestinationPassword = value },
+ { "destinationremote=", "The destination remote name. Defaults to `sourceremote` parameter.", value => options.DestinationRemoteName = value },
+ { "destinationbranch=", "The destination branch name. Defaults to `sourcebranch` parameter.", value => options.DestinationBranchName = value },
{ "f|force", "Force the creation of the PR even if an open PR already exists.", value => options.Force = value != null },
{ "debug", "Print debugging information about the merge but don't actually create the pull request.", value => options.Debug = value != null },
- { "h|help", "Show this message and exit.", value => options.ShowHelp = value != null }
+ { "h|?|help", "Show this message and exit.", value => showHelp = value != null }
};
try
{
parameters.Parse(args);
+ if (showHelp || !options.IsValid)
+ {
+ parameters.WriteOptionDescriptions(Out);
+ return options.IsValid ? 0 : 1;
+ }
+
+ var sourceRepository = RepositoryBase.Create(options.SourceRepoType, options.RepositoryPath, options.SourceRepoName, options.SourceProject, options.SourceUserId, options.SourceUserName, options.SourcePassword, options.SourceRemoteName);
+ var destRepository = RepositoryBase.Create(options.DestinationRepoType, options.RepositoryPath, options.DestinationRepoName, options.DestinationProject, options.DestinationUserId, options.DestinationUserName, options.DestinationPassword, options.DestinationRemoteName);
+ new Program(sourceRepository, destRepository, options).RunAsync().GetAwaiter().GetResult();
+ return 0;
}
catch (OptionException e)
{
- Console.WriteLine($"{exeName}: {e.Message}");
- Console.WriteLine($"Try `{exeName} --help` for more information.");
+ WriteLine($"{exeName}: {e.Message}");
+ WriteLine($"Try `{exeName} --help` for more information.");
return 1;
}
-
- if (options.ShowHelp || !options.AreValid)
- {
- parameters.WriteOptionDescriptions(Console.Out);
- return options.AreValid ? 0 : 1;
- }
- else
- {
- var github = new GitHubClient(new ProductHeaderValue(options.SourceUser));
- github.Credentials = new Credentials(options.AuthToken);
- new Program(options).MakePullRequest().GetAwaiter().GetResult();
- return 0;
- }
}
+ private RepositoryBase _sourceRepo;
+ private RepositoryBase _destRepo;
private Options _options;
- private GitHubClient _client;
- private Program(Options options)
+ private Program(RepositoryBase sourceRepository, RepositoryBase destinationRepository, Options options)
{
+ _sourceRepo = sourceRepository;
+ _destRepo = destinationRepository;
_options = options;
}
- public async Task MakePullRequest()
+ public async Task RunAsync()
{
- _client = new GitHubClient(new ProductHeaderValue(_options.SourceUser));
- _client.Credentials = new Credentials(_options.AuthToken);
- var remoteIntoBranch = await GetShaFromBranch(_options.DestinationUser, _options.RepoName, _options.SourceBranch);
- var newBranchPrefix = $"merge-{_options.SourceBranch}-into-{_options.DestinationBranch}";
- if (!_options.Force && await DoesOpenPrAlreadyExist(newBranchPrefix))
- {
- Console.WriteLine("Existing merge PRs exist; aboring creation. Use `--force` option to override.");
- return;
- }
-
- var newBranchName = await MakePrBranch(_options.SourceUser, _options.RepoName, remoteIntoBranch, newBranchPrefix);
- var pullRequest = await SubmitPullRequest(newBranchName);
-
- // The reason for this delay is twofold:
- //
- // * Github has a bug in which it can "create" a pull request without that pull request
- // being able to be commented on for a short period of time.
- // * The Jenkins "comment watcher" has a bug whereby any comment posted shortly after
- // pull-request creation is ignored.
- //
- // Thus, this delay sidesteps both of those bugs by asking for a VSI test 30 seconds after
- // the creation of the PR. Ugly, yes; but the only *real* way to sidestep this would be to
- // 1) Fix github, 2) Fix jenkins, and while those might be lofty goals, they are not in the
- // scope of this PR.
- await Task.Delay(TimeSpan.FromSeconds(30.0));
-
- await _client.Issue.Comment.Create(_options.DestinationUser, _options.RepoName, pullRequest.Number, "@dotnet-bot test vsi please");
- }
-
- /// The SHA at the tip of `branchName` in the repository `user/repo`
- private async Task GetShaFromBranch(string user, string repo, string branchName)
- {
- var refs = await _client.GitDatabase.Reference.Get(user, repo, $"heads/{branchName}");
- return refs.Object.Sha;
- }
+ await _sourceRepo.Initialize();
+ await _destRepo.Initialize();
- /// True if an existing auto merge PR is still open.
- private async Task DoesOpenPrAlreadyExist(string newBranchPrefix)
- {
- return (await GetExistingMergePrs(newBranchPrefix)).Count > 0;
- }
+ // fetch latest sources
+ WriteLine("Fetching.");
+ _sourceRepo.Fetch(_options.PullRequestBranchSourceRemote);
- /// The existing open merge PRs.
- private async Task> GetExistingMergePrs(string newBranchPrefix)
- {
- var allPullRequests = await _client.PullRequest.GetAllForRepository(_options.DestinationUser, _options.RepoName);
- var openPrs = allPullRequests.Where(pr => pr.Head.Ref.StartsWith(newBranchPrefix) && pr.User.Login == _options.SourceUser).ToList();
+ var (prRepo, prRemoteName, prUserName, prPassword) = _options.PushBranchToDestination
+ ? (_destRepo, _options.DestinationRemoteName, _options.DestinationUserName, _options.DestinationPassword)
+ : (_sourceRepo, _options.SourceRemoteName, _options.SourceUserName, _options.SourcePassword);
- Console.WriteLine($"Found {openPrs.Count} existing open merge pull requests.");
- foreach (var pr in openPrs)
+ // branch from the source
+ var title = $"Merge {_options.SourceBranchName} into {_options.DestinationBranchName}";
+ if (_options.Force || await prRepo.ShouldMakePullRequestAsync(title))
{
- Console.WriteLine($" Open PR: {pr.HtmlUrl}");
+ }
+ else
+ {
+ WriteLine("Existing merge PRs exist; aboring creation.");
+ return;
}
- return openPrs;
- }
-
- ///
- /// Creates a PR branch on the bot account with the branch head at `sha`.
- ///
- /// The name of the branch that was created
- private async Task MakePrBranch(string user, string repo, string sha, string branchNamePrefix)
- {
- var branchName = branchNamePrefix + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss");
+ var prBranchName = $"merge-{_options.SourceBranchName}-into-{_options.DestinationBranchName}-{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss")}";
+ var prSourceBranch = $"{_options.PullRequestBranchSourceRemote}/{_options.SourceBranchName}";
+ WriteLine($"Creating branch '{prBranchName}' from '{prSourceBranch}'.");
+ if (_options.Debug)
+ {
+ WriteLine("Debug: Skiping branch creation.");
+ }
+ else
+ {
+ var prBranch = prRepo.Repository.CreateBranch(prBranchName, prSourceBranch);
+ }
+ // push the branch
+ var remote = prRepo.Repository.Network.Remotes[prRemoteName];
+ WriteLine($"Pushing branch '{prBranchName}'.");
if (_options.Debug)
{
- WriteDebugLine($"Create remote branch '{user}/{repo}/{branchName}' at {sha}");
+ WriteLine("Debug: Skipping branch push.");
}
else
{
- var resp = await _client.Connection.Post(
- uri: new Uri($"https://api.github.com/repos/{user}/{repo}/git/refs"),
- body: $"{{\"ref\": \"refs/heads/{branchName}\", \"sha\": \"{sha}\"}}",
- accepts: "*/*",
- contentType: "application/json");
- var statusCode = resp.HttpResponse.StatusCode;
- if (statusCode != HttpStatusCode.Created)
+ var pushOptions = new PushOptions()
{
- throw new Exception($"Failed creating a new branch {branchName} on {user}/{repo} with code {statusCode}");
- }
+ CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials()
+ {
+ Username = prUserName,
+ Password = prPassword
+ }
+ };
+ prRepo.Repository.Network.Push(remote, $"+refs/heads/{prBranchName}:refs/heads/{prBranchName}", pushOptions);
}
- return branchName;
- }
-
- ///
- /// Creates a pull request
- ///
- private async Task SubmitPullRequest(string newBranchName)
- {
- var remoteName = $"{_options.SourceUser}-{_options.RepoName}";
- var prTitle = $"Merge {_options.SourceBranch} into {_options.DestinationBranch}";
- var prMessage = $@"
-This is an automatically generated pull request from {_options.SourceBranch} into {_options.DestinationBranch}.
-
-@dotnet/roslyn-infrastructure:
-
-``` bash
-git remote add {remoteName} ""https://github.com/{_options.SourceUser}/{_options.RepoName}.git""
-git fetch {remoteName}
-git fetch upstream
-git checkout {newBranchName}
-git reset --hard upstream/{_options.DestinationBranch}
-git merge upstream/{_options.SourceBranch}
-# Fix merge conflicts
-git commit
-git push {remoteName} {newBranchName} --force
-```
-
-Once the merge can be made and all the tests pass, you are free to merge the pull request.
-".Trim();
-
+ // create PR
+ WriteLine("Creating PR.");
if (_options.Debug)
{
- WriteDebugLine($"Create PR with title: {prTitle}.");
- WriteDebugLine($"Create PR with body:\r\n{prMessage}");
- return null;
+ WriteLine("Debug: Skipping PR creation.");
}
else
{
- return await _client.PullRequest.Create(
- owner: _options.DestinationUser,
- name: _options.RepoName,
- newPullRequest: new NewPullRequest(
- title: prTitle,
- head: $"{_options.SourceUser}:{newBranchName}",
- baseRef: _options.DestinationBranch)
- {
- Body = prMessage
- }
- );
+ await _destRepo.CreatePullRequestAsync(title, _options.DestinationRepoOwner, prBranchName, _options.PullRequestBranchSourceRemote, _options.SourceBranchName, _options.DestinationBranchName);
}
}
-
- private void WriteDebugLine(string line)
- {
- Console.WriteLine("Debug: " + line);
- }
}
}
diff --git a/src/Tools/Github/GitMergeBot/README.md b/src/Tools/Github/GitMergeBot/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c02ec2be119aaa15a9ec6c4fbfed0bf1e438bb39
--- /dev/null
+++ b/src/Tools/Github/GitMergeBot/README.md
@@ -0,0 +1,13 @@
+# Examples
+
+## Merge from `https://github.com/dotnet/roslyn`:`dev15-rc2` to `master` where the credentials used belong to a fictional user `merge-bot`.
+
+``` cmd
+GitMergeBot.exe --repopath=C:\path\to\roslyn\repo --sourcetype=GitHub --sourcereponame=roslyn --sourceuser=merge-bot --sourcepassword=super-secret-key --sourceremote=origin --sourcebranch=dev15-rc2 --pushtodestination- --prbranchsourceremote=upstream --destinationrepoowner=dotnet --destinationremote=upstream --destinationbranch=master
+```
+
+## Merge from `https://github.com/Microsoft/visualfsharp`:`master` to `https://`:`microbuild` on a VSO instance where the credentials belong to a fictional user `merge-bot`.
+
+``` cmd
+GitMergeBot.exe --repopath=C:\path\to\fsharp\repo --sourcetype=GitHub --sourcereponame=visualfsharp --sourceuser=merge-bot --sourcepassword=super-secret-key --sourceremote=origin --sourcebranch=master --pushtodestination+ --prbranchsourceremote=upstream --destinationtype=VisualStudioOnline --destinationreponame=FSharp --destinationproject=DevDiv --destinationuserid=[GUID] --destinationuser= --destinationpassword=super-secret-key --destinationremote=vso --destinationbranch=microbuild
+```
diff --git a/src/Tools/Github/GitMergeBot/RepositoryBase.cs b/src/Tools/Github/GitMergeBot/RepositoryBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fdde7a7a52fe84b58b5cb4f8fb43fe01731dafd8
--- /dev/null
+++ b/src/Tools/Github/GitMergeBot/RepositoryBase.cs
@@ -0,0 +1,63 @@
+// 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.Threading.Tasks;
+using LibGit2Sharp;
+
+namespace GitMergeBot
+{
+ internal abstract class RepositoryBase
+ {
+ public Repository Repository { get; }
+ public string RepositoryName { get; }
+ public string UserName { get; }
+ public string Password { get; }
+
+ protected RepositoryBase(string path, string repoName, string userName, string password)
+ {
+ Repository = new Repository(path);
+ RepositoryName = repoName;
+ UserName = userName;
+ Password = password;
+ }
+
+ public virtual Task Initialize()
+ {
+ return Task.CompletedTask;
+ }
+
+ public abstract Task ShouldMakePullRequestAsync(string title);
+ public abstract Task CreatePullRequestAsync(string title, string destinationOwner, string pullRequestBranch, string prBranchSourceRemote, string sourceBranch, string destinationBranch);
+
+ protected void WriteDebugLine(string line)
+ {
+ Console.WriteLine("Debug: " + line);
+ }
+
+ public void Fetch(string remoteName)
+ {
+ var fetchOptions = new FetchOptions()
+ {
+ CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials()
+ {
+ Username = UserName,
+ Password = Password
+ }
+ };
+ Repository.Fetch(remoteName, fetchOptions);
+ }
+
+ public static RepositoryBase Create(RepositoryType type, string path, string repoName, string project, string userId, string userName, string password, string remoteName)
+ {
+ switch (type)
+ {
+ case RepositoryType.GitHub:
+ return new GitHubRepository(path, repoName, userName, password);
+ case RepositoryType.VisualStudioOnline:
+ return new VisualStudioOnlineRepository(path, repoName, project, userId, userName, password, remoteName);
+ default:
+ throw new InvalidOperationException("Unknown repository type.");
+ }
+ }
+ }
+}
diff --git a/src/Tools/Github/GitMergeBot/RepositoryType.cs b/src/Tools/Github/GitMergeBot/RepositoryType.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b0b5638df350c9c5c7842a92a2579e3d2a86a79d
--- /dev/null
+++ b/src/Tools/Github/GitMergeBot/RepositoryType.cs
@@ -0,0 +1,10 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace GitMergeBot
+{
+ internal enum RepositoryType
+ {
+ GitHub,
+ VisualStudioOnline
+ }
+}
diff --git a/src/Tools/Github/GitMergeBot/VisualStudioOnlineRepository.cs b/src/Tools/Github/GitMergeBot/VisualStudioOnlineRepository.cs
new file mode 100644
index 0000000000000000000000000000000000000000..97cbccb1d0f1dbd9e36d9e28b41ff837d307a79f
--- /dev/null
+++ b/src/Tools/Github/GitMergeBot/VisualStudioOnlineRepository.cs
@@ -0,0 +1,142 @@
+// 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.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+
+namespace GitMergeBot
+{
+ internal sealed class VisualStudioOnlineRepository : RepositoryBase
+ {
+ private const string ApiVersion = "3.0";
+
+ private HttpClient _client;
+ private string _project;
+ private string _remoteName;
+ private string _repositoryId;
+ private string _userId;
+
+ public VisualStudioOnlineRepository(string path, string repoName, string project, string userId, string userName, string password, string remoteName)
+ : base(path, repoName, userName, password)
+ {
+ _project = project;
+ _remoteName = remoteName;
+ _userId = userId;
+
+ var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{userName}:{password}"));
+ var remote = Repository.Network.Remotes[remoteName];
+ var remoteUri = new Uri(remote.Url);
+ _client = new HttpClient();
+ _client.BaseAddress = new Uri($"{remoteUri.Scheme}://{remoteUri.Host}");
+ _client.DefaultRequestHeaders.Accept.Clear();
+ _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
+ }
+
+ public override async Task Initialize()
+ {
+ // find the repository ID
+ // https://www.visualstudio.com/en-us/docs/integrate/api/git/repositories#get-a-list-of-repositories
+ var repositories = await GetJsonAsync($"DefaultCollection/{_project}/_apis/git/repositories?api-version={ApiVersion}");
+ _repositoryId = (string)repositories["value"].Single(r => r?["name"].Type == JTokenType.String && (string)r["name"] == RepositoryName)["id"];
+ }
+
+ public override async Task ShouldMakePullRequestAsync(string title)
+ {
+ // https://www.visualstudio.com/en-us/docs/integrate/api/git/pull-requests/pull-requests#get-a-list-of-pull-requests-in-the-repository
+ var foundMatch = false;
+ var result = await GetJsonAsync($"DefaultCollection/_apis/git/repositories/{_repositoryId}/pullRequests?api-version={ApiVersion}&creatorId={_userId}");
+ var pullRequests = (JArray)result["value"];
+ foreach (JObject pr in pullRequests)
+ {
+ if (pr?["repository"]?["name"].Type == JTokenType.String && (string)pr["repository"]["name"] == RepositoryName)
+ {
+ var prTitle = (string)pr["title"];
+ Console.WriteLine($" Open PR: {prTitle}");
+ foundMatch |= prTitle == title;
+ }
+ }
+
+ return !foundMatch;
+ }
+
+ public override async Task CreatePullRequestAsync(string title, string destinationOwner, string pullRequestBranch, string prBranchSourceRemote, string sourceBranch, string destinationBranch)
+ {
+ // https://www.visualstudio.com/en-us/docs/integrate/api/git/pull-requests/pull-requests#create-a-pull-request
+ var prMessage = $@"
+This is an automatically generated pull request from {sourceBranch} into {destinationBranch}.
+
+``` bash
+git remote add {_remoteName} {Repository.Network.Remotes[_remoteName].Url}
+git fetch --all
+git checkout {pullRequestBranch}
+git reset --hard {_remoteName}/{destinationBranch}
+git merge {prBranchSourceRemote}/{sourceBranch}
+# Fix merge conflicts
+git commit
+git push {pullRequestBranch} --force
+```
+
+Once all conflicts are resolved and all the tests pass, you are free to merge the pull request.
+".Trim();
+ var request = new JObject()
+ {
+ ["sourceRefName"] = $"refs/heads/{pullRequestBranch}",
+ ["targetRefName"] = $"refs/heads/{destinationBranch}",
+ ["title"] = title,
+ ["description"] = prMessage,
+ ["reviewers"] = new JArray() // no required reviewers, but necessary for the request
+ };
+ var result = await GetJsonAsync(
+ $"DefaultCollection/_apis/git/repositories/{_repositoryId}/pullRequests?api-version={ApiVersion}",
+ body: request,
+ method: "POST");
+
+ var pullRequestId = (string)result["pullRequestId"];
+
+ // mark the PR to auto complete
+ // https://www.visualstudio.com/en-us/docs/integrate/api/git/pull-requests/pull-requests#auto-complete
+ var autoCompleteRequest = new JObject()
+ {
+ ["autoCompleteSetBy"] = new JObject()
+ {
+ ["id"] = _userId
+ },
+ ["completionOptions"] = new JObject()
+ {
+ ["deleteSourceBranch"] = true,
+ ["mergeCommitMessage"] = $"Pull request #{pullRequestId} auto-completed after passing checks.",
+ ["squashMerge"] = false
+ }
+ };
+
+ result = await GetJsonAsync(
+ $"DefaultCollection/_apis/git/repositories/{_repositoryId}/pullRequests/{pullRequestId}?api-version={ApiVersion}",
+ body: autoCompleteRequest,
+ method: "PATCH");
+ }
+
+ private async Task GetJsonAsync(string requestUri, JObject body = null, string method = "GET")
+ {
+ HttpResponseMessage response;
+ if (body == null)
+ {
+ response = await _client.GetAsync(requestUri);
+ }
+ else
+ {
+ var requestMessage = new HttpRequestMessage(new HttpMethod(method), requestUri);
+ requestMessage.Content = new ByteArrayContent(Encoding.ASCII.GetBytes(body.ToString()));
+ requestMessage.Content.Headers.Add("Content-Type", "application/json");
+ response = await _client.SendAsync(requestMessage);
+ }
+
+ var result = await response.Content.ReadAsStringAsync();
+ return JObject.Parse(result);
+ }
+ }
+}
diff --git a/src/Tools/Github/GitMergeBot/packages.config b/src/Tools/Github/GitMergeBot/packages.config
index f1f10057e91245d34a84ecc3a7408b179e9ab0d0..4a01748c371b14c1c3fb8e8e4f0c21fc5160fa34 100644
--- a/src/Tools/Github/GitMergeBot/packages.config
+++ b/src/Tools/Github/GitMergeBot/packages.config
@@ -1,5 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/PersistentStorageTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/PersistentStorageTests.cs
index 4f361a97d41e09fba72116d0162a5a6dce345630..7c37fe279e02aaedca253865538d5d5faf19d652 100644
--- a/src/VisualStudio/CSharp/Test/PersistentStorage/PersistentStorageTests.cs
+++ b/src/VisualStudio/CSharp/Test/PersistentStorage/PersistentStorageTests.cs
@@ -283,47 +283,6 @@ private void DoSimultaneousReads(Func> read, string expectedValue)
countdown.Wait();
}
- [Fact]
- public async Task PersistentService_IdentifierSet()
- {
- var solution = CreateOrOpenSolution();
-
- var newId = DocumentId.CreateNewId(solution.ProjectIds[0]);
-
- string documentFile = Path.Combine(Path.GetDirectoryName(solution.FilePath), "IdentifierSet.cs");
-
- File.WriteAllText(documentFile, @"
-class A
-{
- public int Test(int i, A a)
- {
- return a;
- }
-}");
-
- var newSolution = solution.AddDocument(DocumentInfo.Create(newId, "IdentifierSet", loader: new FileTextLoader(documentFile, Encoding.UTF8), filePath: documentFile));
-
- using (var storage = GetStorage(newSolution))
- {
- var syntaxTreeStorage = storage as ISyntaxTreeInfoPersistentStorage;
- Assert.NotNull(syntaxTreeStorage);
-
- var document = newSolution.GetDocument(newId);
- var version = await document.GetSyntaxVersionAsync();
- var root = await document.GetSyntaxRootAsync();
-
- Assert.True(syntaxTreeStorage.WriteIdentifierLocations(document, version, root, CancellationToken.None));
-
- Assert.Equal(version, syntaxTreeStorage.GetIdentifierSetVersion(document));
-
- List positions = new List();
- Assert.True(syntaxTreeStorage.ReadIdentifierPositions(document, version, "Test", positions, CancellationToken.None));
-
- Assert.Equal(1, positions.Count);
- Assert.Equal(29, positions[0]);
- }
- }
-
private Solution CreateOrOpenSolution()
{
string solutionFile = Path.Combine(_persistentFolder, "Solution1.sln");
diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/DefinitionsAndReferencesPresenter.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/DefinitionsAndReferencesPresenter.cs
index bdd7606a02ab05fafbfb07c25f3fc878fed13315..99f4586a72016d274bd50c6923ad84f63ef57510 100644
--- a/src/VisualStudio/Core/Def/Implementation/FindReferences/DefinitionsAndReferencesPresenter.cs
+++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/DefinitionsAndReferencesPresenter.cs
@@ -4,7 +4,7 @@
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.VisualStudio.LanguageServices.Implementation.Library.FindResults;
using Microsoft.VisualStudio.Shell;
diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/VisualStudioDefinitionsAndReferencesFactory.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/VisualStudioDefinitionsAndReferencesFactory.cs
index c66ca4fd036dbd1d479f10ea8f8d74fa76f83369..4c8ceff75e13a8c9b5381afd19868eb1172913c9 100644
--- a/src/VisualStudio/Core/Def/Implementation/FindReferences/VisualStudioDefinitionsAndReferencesFactory.cs
+++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/VisualStudioDefinitionsAndReferencesFactory.cs
@@ -4,7 +4,7 @@
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions;
diff --git a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs
index b1fa0f56c08fbfa20e76887c67621c182539d400..2215cea7f0023789936dd2a86651c65defd8b8a3 100644
--- a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs
+++ b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/LibraryManager_FindReferences.cs
@@ -6,7 +6,7 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
diff --git a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs
index c16f7ba5a5717beb9015f80540b15e7d29fba397..ad63ca8c6b79c44191a4867202b0421346abec2f 100644
--- a/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs
+++ b/src/VisualStudio/Core/Def/Implementation/Library/FindResults/TreeItems/DefinitionTreeItem.cs
@@ -4,7 +4,7 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
diff --git a/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs b/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs
index b54b1a8363c4e2e15e2ff3002345fbe6cbe8a73b..7837f7d643ad84ead02e281fc2074b1cad2e8e70 100644
--- a/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs
+++ b/src/VisualStudio/Core/Impl/RoslynVisualStudioWorkspace.cs
@@ -6,12 +6,11 @@
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Undo;
-using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.GeneratedCodeRecognition;
using Microsoft.CodeAnalysis.Options;
using Microsoft.VisualStudio.LanguageServices.Implementation;
@@ -29,16 +28,16 @@ namespace Microsoft.VisualStudio.LanguageServices
internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
{
private readonly IEnumerable> _navigableItemsPresenters;
+ private readonly IEnumerable> _streamingPresenters;
private readonly IEnumerable> _referencedSymbolsPresenters;
- private readonly IEnumerable> _externalDefinitionProviders;
[ImportingConstructor]
private RoslynVisualStudioWorkspace(
SVsServiceProvider serviceProvider,
SaveEventsService saveEventsService,
[ImportMany] IEnumerable> navigableItemsPresenters,
+ [ImportMany] IEnumerable> streamingPresenters,
[ImportMany] IEnumerable> referencedSymbolsPresenters,
- [ImportMany] IEnumerable> externalDefinitionProviders,
[ImportMany] IEnumerable documentOptionsProviderFactories)
: base(
serviceProvider,
@@ -49,8 +48,8 @@ internal class RoslynVisualStudioWorkspace : VisualStudioWorkspaceImpl
InitializeStandardVisualStudioWorkspace(serviceProvider, saveEventsService);
_navigableItemsPresenters = navigableItemsPresenters;
+ _streamingPresenters = streamingPresenters;
_referencedSymbolsPresenters = referencedSymbolsPresenters;
- _externalDefinitionProviders = externalDefinitionProviders;
foreach (var providerFactory in documentOptionsProviderFactories)
{
@@ -183,22 +182,24 @@ private static bool TryResolveSymbol(ISymbol symbol, Project project, Cancellati
return true;
}
- public override bool TryGoToDefinition(ISymbol symbol, Project project, CancellationToken cancellationToken)
+ public override bool TryGoToDefinition(
+ ISymbol symbol, Project project, CancellationToken cancellationToken)
{
- if (!_navigableItemsPresenters.Any())
+ if (!_navigableItemsPresenters.Any() &&
+ !_streamingPresenters.Any())
{
return false;
}
- ISymbol searchSymbol;
- Project searchProject;
- if (!TryResolveSymbol(symbol, project, cancellationToken, out searchSymbol, out searchProject))
+ if (!TryResolveSymbol(symbol, project, cancellationToken,
+ out var searchSymbol, out var searchProject))
{
return false;
}
return GoToDefinitionHelpers.TryGoToDefinition(
- searchSymbol, searchProject, _externalDefinitionProviders, _navigableItemsPresenters, cancellationToken: cancellationToken);
+ searchSymbol, searchProject,
+ _navigableItemsPresenters, _streamingPresenters, cancellationToken);
}
public override bool TryFindAllReferences(ISymbol symbol, Project project, CancellationToken cancellationToken)
@@ -208,9 +209,7 @@ public override bool TryFindAllReferences(ISymbol symbol, Project project, Cance
return false;
}
- ISymbol searchSymbol;
- Project searchProject;
- if (!TryResolveSymbol(symbol, project, cancellationToken, out searchSymbol, out searchProject))
+ if (!TryResolveSymbol(symbol, project, cancellationToken, out var searchSymbol, out var searchProject))
{
return false;
}
@@ -309,4 +308,4 @@ internal override object GetBrowseObject(SymbolListItem symbolListItem)
return null;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/VisualStudio/Core/Next/FindReferences/FindReferencesTableControlEventProcessorProvider.cs b/src/VisualStudio/Core/Next/FindReferences/FindReferencesTableControlEventProcessorProvider.cs
index d0058874f6668b314a15df101a4d691c945d757c..fadb3cff5e17f1c79518227ead654db1b7485607 100644
--- a/src/VisualStudio/Core/Next/FindReferences/FindReferencesTableControlEventProcessorProvider.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/FindReferencesTableControlEventProcessorProvider.cs
@@ -5,7 +5,7 @@
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Text.Classification;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
///
/// Event processor that we export so we can control how navigation works in the streaming
@@ -14,11 +14,11 @@ namespace Microsoft.VisualStudio.LanguageServices.FindReferences
/// ourselves so that we can do things like navigate to MetadataAsSource.
///
[Export(typeof(ITableControlEventProcessorProvider))]
- [DataSourceType(StreamingFindReferencesPresenter.RoslynFindReferencesTableDataSourceSourceTypeIdentifier)]
- [DataSource(StreamingFindReferencesPresenter.RoslynFindReferencesTableDataSourceIdentifier)]
- [Name(nameof(FindReferencesTableControlEventProcessorProvider))]
+ [DataSourceType(StreamingFindUsagesPresenter.RoslynFindUsagesTableDataSourceSourceTypeIdentifier)]
+ [DataSource(StreamingFindUsagesPresenter.RoslynFindUsagesTableDataSourceIdentifier)]
+ [Name(nameof(FindUsagesTableControlEventProcessorProvider))]
[Order(Before = Priority.Default)]
- internal class FindReferencesTableControlEventProcessorProvider : ITableControlEventProcessorProvider
+ internal class FindUsagesTableControlEventProcessorProvider : ITableControlEventProcessorProvider
{
public ITableControlEventProcessor GetAssociatedEventProcessor(
IWpfTableControl tableControl)
diff --git a/src/VisualStudio/Core/Next/FindReferences/ISupportsNavigation.cs b/src/VisualStudio/Core/Next/FindReferences/ISupportsNavigation.cs
index 7436c5da9b9a7c3d05e6b1055981ef742f44591a..bbd27fe9259b46c5e66b7c3fcdf319925a670948 100644
--- a/src/VisualStudio/Core/Next/FindReferences/ISupportsNavigation.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/ISupportsNavigation.cs
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
internal interface ISupportsNavigation
{
bool TryNavigateTo();
}
-}
+}
\ No newline at end of file
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DisposableToolTip.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DisposableToolTip.cs
similarity index 88%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DisposableToolTip.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DisposableToolTip.cs
index c0235c95c4d27cf7568c3296e9bff160e7e37861..e055d25bc18e91298d55f14b0cc2a7cf15897abe 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DisposableToolTip.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DisposableToolTip.cs
@@ -9,9 +9,9 @@
using System.Windows.Controls;
using Microsoft.CodeAnalysis.Editor.Shared.Preview;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
private class DisposableToolTip : IDisposable
{
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DocumentLocationEntry.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DocumentLocationEntry.cs
similarity index 96%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DocumentLocationEntry.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DocumentLocationEntry.cs
index ea06113b2b07c770126ecc85aed74ed30986b9aa..6b3f9421d0ce7f83cb2a9e793075be5ad29665ac 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.DocumentLocationEntry.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.DocumentLocationEntry.cs
@@ -23,15 +23,15 @@
using Microsoft.VisualStudio.Text.Editor;
using Roslyn.Utilities;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
private class DocumentSpanEntry : Entry
{
private static readonly object s_boxedProjectGuid = Guid.Empty;
- private readonly TableDataSourceFindReferencesContext _context;
+ private readonly TableDataSourceFindUsagesContext _context;
private readonly DocumentSpan _documentSpan;
private readonly bool _isDefinitionLocation;
@@ -39,7 +39,7 @@ private class DocumentSpanEntry : Entry
private readonly ClassifiedSpansAndHighlightSpan _classifiedSpans;
public DocumentSpanEntry(
- TableDataSourceFindReferencesContext context,
+ TableDataSourceFindUsagesContext context,
RoslynDefinitionBucket definitionBucket,
DocumentSpan documentSpan,
bool isDefinitionLocation,
@@ -55,7 +55,7 @@ private class DocumentSpanEntry : Entry
_classifiedSpans = classifiedSpans;
}
- private StreamingFindReferencesPresenter Presenter => _context.Presenter;
+ private StreamingFindUsagesPresenter Presenter => _context.Presenter;
private Document Document => _documentSpan.Document;
private TextSpan SourceSpan => _documentSpan.SourceSpan;
@@ -103,7 +103,7 @@ public override bool TryCreateColumnContent(string columnName, out FrameworkElem
}
private static IList GetHighlightedInlines(
- StreamingFindReferencesPresenter presenter,
+ StreamingFindUsagesPresenter presenter,
SourceText sourceText,
ClassifiedSpansAndHighlightSpan classifiedSpansAndHighlight,
bool isDefinition)
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.Entry.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.Entry.cs
similarity index 92%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.Entry.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.Entry.cs
index a96f0283350a2484066ba0163b6825f1856897ff..247ef985cfb759d60516532c43c245d03d468eec 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.Entry.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.Entry.cs
@@ -5,9 +5,9 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.Shell.TableControl;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
///
/// Represents a single entry (i.e. row) in the ungrouped FAR table.
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.LazyToolTip.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.LazyToolTip.cs
similarity index 95%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.LazyToolTip.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.LazyToolTip.cs
index 84cd6cb366f3280fbbfdea5d3cacb4ecd6f5aadf..d6d980b739d4b4208237843f51cc655bbc766406 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.LazyToolTip.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.LazyToolTip.cs
@@ -7,9 +7,9 @@
using System.Windows.Media;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
///
/// Class which allows us to provide a delay-created tooltip for our reference entries.
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.RoslynDefinitionBucket.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.RoslynDefinitionBucket.cs
similarity index 84%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.RoslynDefinitionBucket.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.RoslynDefinitionBucket.cs
index ec6fdab2a9bc02aa595fbf78027a36e060945c83..f92a20e5f532cc0d0f983727fb313c4780480ced 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.RoslynDefinitionBucket.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.RoslynDefinitionBucket.cs
@@ -5,25 +5,25 @@
using System.Windows.Documents;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.VisualStudio.Shell.FindAllReferences;
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Shell.TableManager;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
private class RoslynDefinitionBucket : DefinitionBucket, ISupportsNavigation
{
- private readonly StreamingFindReferencesPresenter _presenter;
- private readonly TableDataSourceFindReferencesContext _context;
+ private readonly StreamingFindUsagesPresenter _presenter;
+ private readonly TableDataSourceFindUsagesContext _context;
public readonly DefinitionItem DefinitionItem;
public RoslynDefinitionBucket(
- StreamingFindReferencesPresenter presenter,
- TableDataSourceFindReferencesContext context,
+ StreamingFindUsagesPresenter presenter,
+ TableDataSourceFindUsagesContext context,
DefinitionItem definitionItem)
: base(name: definitionItem.DisplayParts.JoinText() + " " + definitionItem.GetHashCode(),
sourceTypeIdentifier: context.SourceTypeIdentifier,
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.SimpleMessageEntry.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.SimpleMessageEntry.cs
similarity index 90%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.SimpleMessageEntry.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.SimpleMessageEntry.cs
index 8f7812c54f38080af898f3df7a5f40b8328e7b24..75a4a6f2ab6d0e1059118d8efbec7a0373f1bf2b 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.SimpleMessageEntry.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.SimpleMessageEntry.cs
@@ -4,9 +4,9 @@
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Shell.TableManager;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
private class SimpleMessageEntry : Entry
{
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableDataSourceFindUsagesContext.cs
similarity index 97%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableDataSourceFindUsagesContext.cs
index a2009c662baf6890730327b41dbe41c02deaec82..26e08c97ac29d568fc62bb56ef6301afac5d9205 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableDataSourceFindReferencesContext.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableDataSourceFindUsagesContext.cs
@@ -11,27 +11,26 @@
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Editor;
-using Microsoft.CodeAnalysis.FindReferences;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
-using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Shell.FindAllReferences;
using Microsoft.VisualStudio.Shell.TableManager;
using Roslyn.Utilities;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
- private class TableDataSourceFindReferencesContext :
- FindReferencesContext, ITableDataSource, ITableEntriesSnapshotFactory
+ private class TableDataSourceFindUsagesContext :
+ FindUsagesContext, ITableDataSource, ITableEntriesSnapshotFactory
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private ITableDataSink _tableDataSink;
- public readonly StreamingFindReferencesPresenter Presenter;
+ public readonly StreamingFindUsagesPresenter Presenter;
private readonly IFindAllReferencesWindow _findReferencesWindow;
// Lock which protects _definitionToShoudlShowWithoutReferences,
@@ -55,8 +54,8 @@ private class TableDataSourceFindReferencesContext :
private TableEntriesSnapshot _lastSnapshot;
public int CurrentVersionNumber { get; private set; }
- public TableDataSourceFindReferencesContext(
- StreamingFindReferencesPresenter presenter,
+ public TableDataSourceFindUsagesContext(
+ StreamingFindUsagesPresenter presenter,
IFindAllReferencesWindow findReferencesWindow)
{
presenter.AssertIsForeground();
@@ -98,9 +97,9 @@ internal void OnSubscriptionDisposed()
public string DisplayName => "Roslyn Data Source";
- public string Identifier => RoslynFindReferencesTableDataSourceIdentifier;
+ public string Identifier => RoslynFindUsagesTableDataSourceIdentifier;
- public string SourceTypeIdentifier => RoslynFindReferencesTableDataSourceSourceTypeIdentifier;
+ public string SourceTypeIdentifier => RoslynFindUsagesTableDataSourceSourceTypeIdentifier;
public IDisposable Subscribe(ITableDataSink sink)
{
@@ -117,7 +116,7 @@ public IDisposable Subscribe(ITableDataSink sink)
#endregion
- #region FindReferencesContext overrides.
+ #region FindUsagesContext overrides.
public override void SetSearchLabel(string displayName)
{
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableEntriesSnapshot.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableEntriesSnapshot.cs
similarity index 93%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableEntriesSnapshot.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableEntriesSnapshot.cs
index b8c153c74620b05671180f17f91ca3600c46f7c3..fa2e9249c6cc07c157c0e798a5d2329a30f47b16 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.TableEntriesSnapshot.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.TableEntriesSnapshot.cs
@@ -5,9 +5,9 @@
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Shell.TableManager;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- internal partial class StreamingFindReferencesPresenter
+ internal partial class StreamingFindUsagesPresenter
{
private class TableEntriesSnapshot : WpfTableEntriesSnapshotBase
{
diff --git a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.cs b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs
similarity index 79%
rename from src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.cs
rename to src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs
index 2adf28b695ff92298a89d57167ac813abd0faaab..67250920eab22558582a40d18c0f0c1a0d6a0b78 100644
--- a/src/VisualStudio/Core/Next/FindReferences/StreamingFindReferencesPresenter.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/StreamingFindUsagesPresenter.cs
@@ -14,18 +14,19 @@
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Language.Intellisense;
+using Microsoft.CodeAnalysis.FindUsages;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
- [Export(typeof(IStreamingFindReferencesPresenter)), Shared]
- internal partial class StreamingFindReferencesPresenter :
- ForegroundThreadAffinitizedObject, IStreamingFindReferencesPresenter
+ [Export(typeof(IStreamingFindUsagesPresenter)), Shared]
+ internal partial class StreamingFindUsagesPresenter :
+ ForegroundThreadAffinitizedObject, IStreamingFindUsagesPresenter
{
- public const string RoslynFindReferencesTableDataSourceIdentifier =
- nameof(RoslynFindReferencesTableDataSourceIdentifier);
+ public const string RoslynFindUsagesTableDataSourceIdentifier =
+ nameof(RoslynFindUsagesTableDataSourceIdentifier);
- public const string RoslynFindReferencesTableDataSourceSourceTypeIdentifier =
- nameof(RoslynFindReferencesTableDataSourceSourceTypeIdentifier);
+ public const string RoslynFindUsagesTableDataSourceSourceTypeIdentifier =
+ nameof(RoslynFindUsagesTableDataSourceSourceTypeIdentifier);
private readonly IServiceProvider _serviceProvider;
@@ -40,7 +41,7 @@ internal partial class StreamingFindReferencesPresenter :
private readonly IFindAllReferencesService _vsFindAllReferencesService;
[ImportingConstructor]
- public StreamingFindReferencesPresenter(
+ public StreamingFindUsagesPresenter(
Shell.SVsServiceProvider serviceProvider,
ITextBufferFactoryService textBufferFactoryService,
IProjectionBufferFactoryService projectionBufferFactoryService,
@@ -63,15 +64,15 @@ internal partial class StreamingFindReferencesPresenter :
_vsFindAllReferencesService = (IFindAllReferencesService)_serviceProvider.GetService(typeof(SVsFindAllReferences));
}
- public FindReferencesContext StartSearch()
+ public FindUsagesContext StartSearch(string title)
{
this.AssertIsForeground();
// Get the appropriate window for FAR results to go into.
- var window = _vsFindAllReferencesService.StartSearch(label: null);
+ var window = _vsFindAllReferencesService.StartSearch(title);
// Make the data source that will feed data into this window.
- var dataSource = new TableDataSourceFindReferencesContext(this, window);
+ var dataSource = new TableDataSourceFindUsagesContext(this, window);
// And return the data source so that the FindRefs engine can report results
// which the data source can then create the appropriate presentation items for
diff --git a/src/VisualStudio/Core/Next/FindReferences/TaggedTextAndHighlightSpan.cs b/src/VisualStudio/Core/Next/FindReferences/TaggedTextAndHighlightSpan.cs
index 1c78f7ec7eb0060a7056775b2222bad169554783..c184647dce23c676d2c87d60a18b7d7f5df66d14 100644
--- a/src/VisualStudio/Core/Next/FindReferences/TaggedTextAndHighlightSpan.cs
+++ b/src/VisualStudio/Core/Next/FindReferences/TaggedTextAndHighlightSpan.cs
@@ -4,7 +4,7 @@
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Text;
-namespace Microsoft.VisualStudio.LanguageServices.FindReferences
+namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
internal struct ClassifiedSpansAndHighlightSpan
{
diff --git a/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj b/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj
index 811919bbf751d1da837ec64a67a9b6129131314a..da846eb31dfbc77d3e2836bfa5528e46dbc6d6cb 100644
--- a/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj
+++ b/src/VisualStudio/Core/Next/ServicesVisualStudio.Next.csproj
@@ -84,15 +84,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/VisualStudio/Core/Test/FindResults/FindResultsTests.vb b/src/VisualStudio/Core/Test/FindResults/FindResultsTests.vb
index 80a5a39af2be9471b308d4b3ba5553fae7313011..bf153765e6b1e6ee67bab86d53e20382449647a8 100644
--- a/src/VisualStudio/Core/Test/FindResults/FindResultsTests.vb
+++ b/src/VisualStudio/Core/Test/FindResults/FindResultsTests.vb
@@ -6,7 +6,7 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
-Imports Microsoft.CodeAnalysis.FindReferences
+Imports Microsoft.CodeAnalysis.FindUsages
Imports Microsoft.CodeAnalysis.FindSymbols
Imports Microsoft.VisualStudio.Composition
Imports Microsoft.VisualStudio.LanguageServices.Implementation.Library.FindResults
diff --git a/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb b/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb
index 1de8421d0ed945260b14e678b3a7bb987128bfb8..583e43014c45ef11287eeb582c80a02a85403b1f 100644
--- a/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb
+++ b/src/VisualStudio/Core/Test/GoToDefinition/GoToDefinitionApiTests.vb
@@ -2,9 +2,8 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis
-Imports Microsoft.CodeAnalysis.Editor
+Imports Microsoft.CodeAnalysis.Editor.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.Host
-Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Roslyn.Test.Utilities
@@ -39,7 +38,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.GoToDefinition
WpfTestCase.RequireWpfFact($"{NameOf(GoToDefinitionHelpers)}.{NameOf(GoToDefinitionHelpers.TryGoToDefinition)} assumes it's on the UI thread with a WaitAndGetResult call")
Dim success = GoToDefinitionHelpers.TryGoToDefinition(
- symbolInfo.Symbol, document.Project, {}, {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}, thirdPartyNavigationAllowed:=True, throwOnHiddenDefinition:=False, cancellationToken:=CancellationToken.None)
+ symbolInfo.Symbol, document.Project,
+ {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}, {},
+ thirdPartyNavigationAllowed:=True, throwOnHiddenDefinition:=False, cancellationToken:=CancellationToken.None)
Assert.Equal(expectSuccess, success)
End Using
diff --git a/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs
index e361aff04bedecb889a8a66d99d59f1568cd5313..86576708f7e35bed9487cb3fde4823dfd08a8254 100644
--- a/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs
+++ b/src/Workspaces/CSharp/Portable/CodeStyle/CSharpCodeStyleOptions.cs
@@ -10,16 +10,20 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeStyle
internal static class CSharpCodeStyleOptions
{
// TODO: get sign off on public api changes.
- public static readonly Option UseVarWhenDeclaringLocals = new Option(nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), defaultValue: true,
+ public static readonly Option UseVarWhenDeclaringLocals = new Option(
+ nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseVarWhenDeclaringLocals"));
- public static readonly Option> UseImplicitTypeForIntrinsicTypes = new Option>(nameof(CodeStyleOptions), nameof(UseImplicitTypeForIntrinsicTypes), defaultValue: CodeStyleOption.Default,
+ public static readonly Option> UseImplicitTypeForIntrinsicTypes = new Option>(
+ nameof(CodeStyleOptions), nameof(UseImplicitTypeForIntrinsicTypes), defaultValue: CodeStyleOption.Default,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeForIntrinsicTypes"));
- public static readonly Option> UseImplicitTypeWhereApparent = new Option>(nameof(CodeStyleOptions), nameof(UseImplicitTypeWhereApparent), defaultValue: CodeStyleOption.Default,
+ public static readonly Option> UseImplicitTypeWhereApparent = new Option>(
+ nameof(CodeStyleOptions), nameof(UseImplicitTypeWhereApparent), defaultValue: CodeStyleOption.Default,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeWhereApparent"));
- public static readonly Option> UseImplicitTypeWherePossible = new Option>(nameof(CodeStyleOptions), nameof(UseImplicitTypeWherePossible), defaultValue: CodeStyleOption.Default,
+ public static readonly Option> UseImplicitTypeWherePossible = new Option>(
+ nameof(CodeStyleOptions), nameof(UseImplicitTypeWherePossible), defaultValue: CodeStyleOption.Default,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeWherePossible"));
public static readonly Option> PreferConditionalDelegateCall = new Option>(nameof(CodeStyleOptions), nameof(PreferConditionalDelegateCall), defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
diff --git a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs
index b53c2a9ca55ecb46573d8e42b9d1ec3b1480e138..bb2317df6ea42f97db3b8aa57c03e6c6422984d0 100644
--- a/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs
+++ b/src/Workspaces/CSharp/Portable/Extensions/ContextQuery/SyntaxTreeExtensions.cs
@@ -292,6 +292,46 @@ public static bool IsAttributeNameContext(this SyntaxTree syntaxTree, int positi
return false;
}
+ public static bool IsLocalFunctionDeclarationContext(
+ this SyntaxTree syntaxTree,
+ int position,
+ CancellationToken cancellationToken)
+ {
+ var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken);
+ var token = leftToken.GetPreviousTokenIfTouchingWord(position);
+
+ // Local functions are always valid in a statement context
+ if (syntaxTree.IsStatementContext(position, leftToken, cancellationToken))
+ {
+ return true;
+ }
+
+ // Also valid after certain modifiers
+ var validModifiers = SyntaxKindSet.LocalFunctionModifiers;
+
+ var modifierTokens = syntaxTree.GetPrecedingModifiers(
+ position, token, out int beforeModifiersPosition);
+
+ if (modifierTokens.IsSubsetOf(validModifiers))
+ {
+ if (token.HasMatchingText(SyntaxKind.AsyncKeyword))
+ {
+ // second appearance of "async" not followed by modifier: treat as type
+ if (syntaxTree.GetPrecedingModifiers(token.SpanStart, token, cancellationToken)
+ .Contains(SyntaxKind.AsyncKeyword))
+ {
+ return false;
+ }
+ }
+
+ leftToken = syntaxTree.FindTokenOnLeftOfPosition(beforeModifiersPosition, cancellationToken);
+ token = leftToken.GetPreviousTokenIfTouchingWord(beforeModifiersPosition);
+ return syntaxTree.IsStatementContext(beforeModifiersPosition, token, cancellationToken);
+ }
+
+ return false;
+ }
+
public static bool IsTypeDeclarationContext(
this SyntaxTree syntaxTree, int position, SyntaxToken tokenOnLeftOfPosition, CancellationToken cancellationToken)
{
diff --git a/src/Workspaces/CSharp/Portable/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ITypeSymbolExtensions.cs
index 1443ba3fa3ae921aca1656bb61c1d44d6e95cfba..2e68bb14e5109c31463559f2825f5b7a32e5f170 100644
--- a/src/Workspaces/CSharp/Portable/Extensions/ITypeSymbolExtensions.cs
+++ b/src/Workspaces/CSharp/Portable/Extensions/ITypeSymbolExtensions.cs
@@ -6,9 +6,11 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ErrorReporting;
+using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
@@ -133,5 +135,34 @@ public static bool IsIntrinsicType(this ITypeSymbol typeSymbol)
return false;
}
}
+
+ public static TypeSyntax GenerateTypeSyntaxOrVar(
+ this ITypeSymbol symbol, OptionSet options, bool typeIsApperant)
+ {
+ var useVar = IsVarDesired(symbol, options, typeIsApperant);
+
+ return useVar
+ ? SyntaxFactory.IdentifierName("var")
+ : symbol.GenerateTypeSyntax();
+ }
+
+ private static bool IsVarDesired(ITypeSymbol type, OptionSet options, bool typeIsApperant)
+ {
+ // If they want it for intrinsics, and this is an intrinsic, then use var.
+ if (type.IsSpecialType() == true)
+ {
+ return options.GetOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes).Value;
+ }
+
+ // If they want it only for apperant types, then only use "var" if the caller
+ // says the type was apperant.
+ if (typeIsApperant)
+ {
+ return options.GetOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent).Value;
+ }
+
+ // If they want "var" whenever possible, then use "var".
+ return options.GetOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible).Value;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
index d7b5bbf0fc257b9a5f524cba8d0b66a5f453981a..3b18d66726392319992a9248820015fbe5108375 100644
--- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
+++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxTreeExtensions.cs
@@ -16,7 +16,17 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions
internal static partial class SyntaxTreeExtensions
{
public static ISet GetPrecedingModifiers(
- this SyntaxTree syntaxTree, int position, SyntaxToken tokenOnLeftOfPosition, CancellationToken cancellationToken)
+ this SyntaxTree syntaxTree,
+ int position,
+ SyntaxToken tokenOnLeftOfPosition,
+ CancellationToken cancellationToken)
+ => syntaxTree.GetPrecedingModifiers(position, tokenOnLeftOfPosition, out var _);
+
+ public static ISet GetPrecedingModifiers(
+ this SyntaxTree syntaxTree,
+ int position,
+ SyntaxToken tokenOnLeftOfPosition,
+ out int positionBeforeModifiers)
{
var token = tokenOnLeftOfPosition;
token = token.GetPreviousTokenIfTouchingWord(position);
@@ -58,6 +68,7 @@ internal static partial class SyntaxTreeExtensions
break;
}
+ positionBeforeModifiers = token.FullSpan.End;
return result;
}
diff --git a/src/Workspaces/CSharp/Portable/Extensions/TypeSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/TypeSyntaxExtensions.cs
index 6081a8d26236e2d694714cbde840d1eba98900f4..16c169cea54ea6b750560580b0e044a313b216e4 100644
--- a/src/Workspaces/CSharp/Portable/Extensions/TypeSyntaxExtensions.cs
+++ b/src/Workspaces/CSharp/Portable/Extensions/TypeSyntaxExtensions.cs
@@ -2,11 +2,9 @@
using System.Linq;
using System.Threading;
-using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
-using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
@@ -93,4 +91,4 @@ public static bool IsTypeInferred(this TypeSyntax typeSyntax, SemanticModel sema
return true;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Workspaces/CSharp/Portable/Utilities/SyntaxKindSet.cs b/src/Workspaces/CSharp/Portable/Utilities/SyntaxKindSet.cs
index 6504971282790f902abbe8655954b69fb2c5f32d..4207b933c72cd87503ddaae7d1695d0c0bac0afd 100644
--- a/src/Workspaces/CSharp/Portable/Utilities/SyntaxKindSet.cs
+++ b/src/Workspaces/CSharp/Portable/Utilities/SyntaxKindSet.cs
@@ -52,6 +52,12 @@ internal class SyntaxKindSet
SyntaxKind.VolatileKeyword,
};
+ public static readonly ISet LocalFunctionModifiers = new HashSet(SyntaxFacts.EqualityComparer)
+ {
+ SyntaxKind.AsyncKeyword,
+ SyntaxKind.UnsafeKeyword
+ };
+
public static readonly ISet AllTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer)
{
SyntaxKind.InterfaceDeclaration,
diff --git a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt
index f35ad7b9e3f2de60fa6a94f207d4284ab18240ed..c98bf9b55c78ef739f432256719c24840367c9b3 100644
--- a/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt
+++ b/src/Workspaces/Core/Desktop/PublicAPI.Unshipped.txt
@@ -1 +1,2 @@
-Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Diagnostics.get -> System.Collections.Immutable.ImmutableList
\ No newline at end of file
+static Microsoft.CodeAnalysis.Host.Mef.DesktopMefHostServices.DefaultAssemblies.get -> System.Collections.Immutable.ImmutableArray