diff --git a/CHANGELOG.md b/CHANGELOG.md index a58d92a5ee..d1f8e9d090 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,25 @@ # Changelog All changes to the project will be documented in this file. -## [1.32.9] - not yet released +## [1.32.12] - not released yet +* Include constant values in `/typelookup` responses ([omnisharp-vscode#2857](https://github.com/OmniSharp/omnisharp-vscode/issues/2857), PR: [#1420](https://github.com/OmniSharp/omnisharp-roslyn/pull/1420)) + +## [1.32.11] - 2019-02-27 +* Updated to Roslyn `3.0.0-beta4-19126-05` to match VS 16.0p4 ([#1413](https://github.com/OmniSharp/omnisharp-roslyn/issues/1413), PR: [#1414](https://github.com/OmniSharp/omnisharp-roslyn/pull/1414)) +* Added support for reading C# 8.0 `NullableContextOptions` from csproj files ([#1396](https://github.com/OmniSharp/omnisharp-roslyn/issues/1396), PR: [#1404](https://github.com/OmniSharp/omnisharp-roslyn/pull/1404)) + +## [1.32.10] - 2019-01-25 +* Updated to Roslyn 3.0 to match [VS 2019](https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-preview#VS2019_Preview2) (PR: [#1391](https://github.com/OmniSharp/omnisharp-roslyn/pull/1391)) +* Fixed shutdown event handling for LSP _(Contributed by [@LoneBoco](https://github.com/LoneBoco))_ ([#1113](https://github.com/OmniSharp/omnisharp-roslyn/issues/1113), PR: [#1345](https://github.com/OmniSharp/omnisharp-roslyn/pull/1345)) + +## [1.32.9] - 2019-1-22 * Updated to Roslyn `2.11.0-beta1-final` and initial support for C# 8 (PR: [#1365](https://github.com/OmniSharp/omnisharp-roslyn/pull/1365)) * Incorporate *IndentSwitchCaseSectionWhenBlock* into OmniSharp's formatting options. This fixes the default formatting behavior, as the setting is set to *true* by default, and still allows users to disable it if needed. ([#1351](https://github.com/OmniSharp/omnisharp-roslyn/issues/1351), PR: [#1353](https://github.com/OmniSharp/omnisharp-roslyn/pull/1353)) * Removed unused `-stdio` flag from the `StdioCommandLineApplication` (PR: [#1362](https://github.com/OmniSharp/omnisharp-roslyn/pull/1362)) * Fixed finding references to operator overloads _(Contributed by [@SirIntruder](https://github.com/SirIntruder))_ (PR: [#1371](https://github.com/OmniSharp/omnisharp-roslyn/pull/1371)) +* Fixed a 1.29.0 regression that caused LSP not to work with `StdioCommandLineApplication` ([#1269](https://github.com/OmniSharp/omnisharp-roslyn/issues/1269), PR: [#1346](https://github.com/OmniSharp/omnisharp-roslyn/pull/1346)) +* Improved handling of files moving on disk (PR: [#1368](https://github.com/OmniSharp/omnisharp-roslyn/pull/1368)) +* Improved detection of MSBuild when multiple instances are available _(Contributed by [@johnnyasantoss ](https://github.com/johnnyasantoss))_ (PR: [#1349](https://github.com/OmniSharp/omnisharp-roslyn/pull/1349)) ## [1.32.8] - 2018-11-14 * Fixed MSBuild discovery path (1.32.7 regression) (PR: [#1337](https://github.com/OmniSharp/omnisharp-roslyn/pull/1337)) diff --git a/NuGet.Config b/NuGet.Config index ab6af752a0..0c203bfcab 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,5 +4,7 @@ + + diff --git a/OmniSharp.sln b/OmniSharp.sln index 90b00b4ba6..16abae3947 100644 --- a/OmniSharp.sln +++ b/OmniSharp.sln @@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject build.cake = build.cake build.json = build.json + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets global.json = global.json NuGet.Config = NuGet.Config tools\packages.config = tools\packages.config @@ -29,8 +31,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Stdio.Tests", "te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Abstractions", "src\OmniSharp.Abstractions\OmniSharp.Abstractions.csproj", "{0C54BE83-EF9E-4419-B654-A0760745BF80}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Plugins", "src\OmniSharp.Plugins\OmniSharp.Plugins.csproj", "{1C9AE254-6076-4EE6-80FD-B0AE4E16347D}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Roslyn.CSharp", "src\OmniSharp.Roslyn.CSharp\OmniSharp.Roslyn.CSharp.csproj", "{ED1A82CB-2741-45F5-A28B-7C36EFDF9746}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Roslyn", "src\OmniSharp.Roslyn\OmniSharp.Roslyn.csproj", "{4F5FC4AF-3977-4ECB-9B58-D16E8024BC97}" @@ -73,6 +73,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Stdio.Driver", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OmniSharp.Script.Tests", "tests\OmniSharp.Script.Tests\OmniSharp.Script.Tests.csproj", "{9E4BA68C-7F4B-429A-A0C7-8CE7D41D610F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OmniSharp.Shared", "src\OmniSharp.Shared\OmniSharp.Shared.csproj", "{9571E3FE-E742-44AC-9E1F-64156815B8E1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -131,18 +133,6 @@ Global {0C54BE83-EF9E-4419-B654-A0760745BF80}.Release|x64.Build.0 = Release|Any CPU {0C54BE83-EF9E-4419-B654-A0760745BF80}.Release|x86.ActiveCfg = Release|Any CPU {0C54BE83-EF9E-4419-B654-A0760745BF80}.Release|x86.Build.0 = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|x64.ActiveCfg = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|x64.Build.0 = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|x86.ActiveCfg = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Debug|x86.Build.0 = Debug|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|Any CPU.Build.0 = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|x64.ActiveCfg = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|x64.Build.0 = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|x86.ActiveCfg = Release|Any CPU - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D}.Release|x86.Build.0 = Release|Any CPU {ED1A82CB-2741-45F5-A28B-7C36EFDF9746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED1A82CB-2741-45F5-A28B-7C36EFDF9746}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED1A82CB-2741-45F5-A28B-7C36EFDF9746}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -395,6 +385,18 @@ Global {9E4BA68C-7F4B-429A-A0C7-8CE7D41D610F}.Release|x64.Build.0 = Release|Any CPU {9E4BA68C-7F4B-429A-A0C7-8CE7D41D610F}.Release|x86.ActiveCfg = Release|Any CPU {9E4BA68C-7F4B-429A-A0C7-8CE7D41D610F}.Release|x86.Build.0 = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|x64.ActiveCfg = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|x64.Build.0 = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|x86.ActiveCfg = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Debug|x86.Build.0 = Debug|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|Any CPU.Build.0 = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|x64.ActiveCfg = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|x64.Build.0 = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|x86.ActiveCfg = Release|Any CPU + {9571E3FE-E742-44AC-9E1F-64156815B8E1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -404,7 +406,6 @@ Global {A4F5FE6B-D3B4-4B63-A949-44E12AB8D535} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {AB5A975C-378B-45DC-AD69-50D808338AC2} = {35E025BF-BBB2-4FAC-9F4B-37CBA083EE47} {0C54BE83-EF9E-4419-B654-A0760745BF80} = {2C348365-A9D8-459E-9276-56FC46AAEE31} - {1C9AE254-6076-4EE6-80FD-B0AE4E16347D} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {ED1A82CB-2741-45F5-A28B-7C36EFDF9746} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {4F5FC4AF-3977-4ECB-9B58-D16E8024BC97} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {9AF025CA-3706-401F-8D50-59FAD5AFE725} = {2C348365-A9D8-459E-9276-56FC46AAEE31} @@ -426,6 +427,7 @@ Global {BC640CBF-F6E2-42EA-9D61-FB6E515AEA44} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {D2A78CEE-B278-476F-AF34-A7D6F792F973} = {2C348365-A9D8-459E-9276-56FC46AAEE31} {9E4BA68C-7F4B-429A-A0C7-8CE7D41D610F} = {35E025BF-BBB2-4FAC-9F4B-37CBA083EE47} + {9571E3FE-E742-44AC-9E1F-64156815B8E1} = {2C348365-A9D8-459E-9276-56FC46AAEE31} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4DD725CE-B49A-4151-8B77-BB33FE88E46E} diff --git a/README.md b/README.md index 6980e04bcd..8e7017f852 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@ OmniSharp-Roslyn is a .NET development platform based on [Roslyn](https://github OmniSharp-Roslyn is built with the [.NET Core SDK](https://dot.net/) on Windows and [Mono](http://www.mono-project.com/) on OSX/Linux. It targets the __net461__ target framework. OmniSharp requires __mono__ (>=5.2.0) if it is run on a platform other than Windows. -For Arch Linux users, need package [msbuild-stable](https://aur.archlinux.org/packages/msbuild-stable/) (>= 15.0) +For Arch Linux users, need package [msbuild-stable](https://aur.archlinux.org/packages/msbuild-stable/) (>= 15.0). In addition, if you need the HTTP interface and you want to run on Linux, you'll also need to make sure that you have [libuv](http://libuv.org) installed. +See also https://github.com/OmniSharp/omnisharp-roslyn/issues/1202#issuecomment-421543905 . + ## What's new See our [change log](https://github.com/OmniSharp/omnisharp-roslyn/blob/master/CHANGELOG.md) for all of the updates. diff --git a/build.cake b/build.cake index c06bc0cd24..cb17aab3d8 100644 --- a/build.cake +++ b/build.cake @@ -48,7 +48,7 @@ bool AllowLegacyTests() if (platform.IsLinux) { - var version = platform.Version.ToString(); + var version = platform.Version?.ToString(); // Taken from https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/dotnet-install.sh switch (platform.DistroName) @@ -638,8 +638,6 @@ Task("Test") // This is necessary to work around a Mono bug that is exasperated by xUnit. DirectoryHelper.Copy($"{env.Folders.MonoMSBuildLib}", instanceFolder); - DeleteUnnecessaryAssemblies(instanceFolder); - var runScript = CombinePaths(env.Folders.Mono, "run"); // By default, the run script launches OmniSharp. To launch our Mono runtime @@ -655,25 +653,6 @@ Task("Test") } }); -/// -/// Delete assemblies that are included in our Mono package. -/// -void DeleteUnnecessaryAssemblies(string folder) -{ - FileHelper.Delete(CombinePaths(folder, "System.AppContext.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Numerics.Vectors.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Runtime.InteropServices.RuntimeInformation.dll")); - FileHelper.Delete(CombinePaths(folder, "System.ComponentModel.Primitives.dll")); - FileHelper.Delete(CombinePaths(folder, "System.ComponentModel.TypeConverter.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Console.dll")); - FileHelper.Delete(CombinePaths(folder, "System.IO.FileSystem.Primitives.dll")); - FileHelper.Delete(CombinePaths(folder, "System.IO.FileSystem.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Security.Cryptography.Encoding.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Security.Cryptography.Primitives.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Security.Cryptography.X509Certificates.dll")); - FileHelper.Delete(CombinePaths(folder, "System.Threading.Thread.dll")); -} - void CopyMonoBuild(BuildEnvironment env, string sourceFolder, string outputFolder) { DirectoryHelper.Copy(sourceFolder, outputFolder, copySubDirectories: false); @@ -681,8 +660,6 @@ void CopyMonoBuild(BuildEnvironment env, string sourceFolder, string outputFolde // Copy MSBuild runtime and libraries DirectoryHelper.Copy($"{env.Folders.MSBuild}", CombinePaths(outputFolder, "msbuild")); - // Included in Mono - DeleteUnnecessaryAssemblies(outputFolder); } void CopyExtraDependencies(BuildEnvironment env, string outputFolder) diff --git a/build.json b/build.json index d3cf93632e..70307aa9eb 100644 --- a/build.json +++ b/build.json @@ -38,7 +38,8 @@ "CSharpAndFSharp", "ProjectWithMismatchedFileName", "SolutionWithSignedProject", - "ProjectWithMultiTFMLib" + "ProjectWithMultiTFMLib", + "ExternAlias" ], "LegacyTestAssets": [ "LegacyNUnitTestProject", diff --git a/build/Packages.props b/build/Packages.props index 58540edeab..8df4b99402 100644 --- a/build/Packages.props +++ b/build/Packages.props @@ -4,7 +4,7 @@ 15.8.166 4.8.0 - 2.11.0-beta1-final + 3.0.0-beta4-19126-05 2.4.0 @@ -60,13 +60,17 @@ + + - + + + diff --git a/build/Settings.props b/build/Settings.props index 443cce0df8..62b54e1687 100644 --- a/build/Settings.props +++ b/build/Settings.props @@ -2,7 +2,7 @@ - 7.2 + 7.3 true Debug true diff --git a/global.json b/global.json index 4628116978..7455df8748 100644 --- a/global.json +++ b/global.json @@ -2,4 +2,4 @@ "sdk": { "version": "2.1.301" } -} +} \ No newline at end of file diff --git a/src/OmniSharp.Abstractions/Configuration.cs b/src/OmniSharp.Abstractions/Configuration.cs index 172fdee5a9..c83160d21e 100644 --- a/src/OmniSharp.Abstractions/Configuration.cs +++ b/src/OmniSharp.Abstractions/Configuration.cs @@ -4,7 +4,7 @@ internal static class Configuration { public static bool ZeroBasedIndices = false; - public const string RoslynVersion = "2.11.0.0"; + public const string RoslynVersion = "3.0.0.0"; public const string RoslynPublicKeyToken = "31bf3856ad364e35"; public readonly static string RoslynFeatures = GetRoslynAssemblyFullName("Microsoft.CodeAnalysis.Features"); diff --git a/src/OmniSharp.Abstractions/Eventing/IEventEmitterExtensions.cs b/src/OmniSharp.Abstractions/Eventing/IEventEmitterExtensions.cs index 0d093a4502..ef2d19fc37 100644 --- a/src/OmniSharp.Abstractions/Eventing/IEventEmitterExtensions.cs +++ b/src/OmniSharp.Abstractions/Eventing/IEventEmitterExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using OmniSharp.Models.Events; +using OmniSharp; +using OmniSharp.Models; +using System.Linq; namespace OmniSharp.Eventing { @@ -41,5 +44,24 @@ public static void UnresolvedDepdendencies(this IEventEmitter emitter, string pr UnresolvedDependencies = unresolvedDependencies }); } + + public static void ProjectInformation(this IEventEmitter emitter, + HashedString projectGuid, + IEnumerable targetFrameworks, + IEnumerable references, + IEnumerable fileExtensions) + { + var projectConfiguration = new ProjectConfigurationMessage() + { + TargetFrameworks = targetFrameworks.Select(hashed => hashed.Value), + ProjectGuid = projectGuid.Value, + References = references.Select(hashed => hashed.Value), + FileExtensions = fileExtensions.Select(hashed => hashed.Value) + }; + + emitter.Emit( + EventTypes.ProjectConfiguration, + projectConfiguration); + } } } diff --git a/src/OmniSharp.Abstractions/Models/Events/EventTypes.cs b/src/OmniSharp.Abstractions/Models/Events/EventTypes.cs index ff36e5961f..8d68a21401 100644 --- a/src/OmniSharp.Abstractions/Models/Events/EventTypes.cs +++ b/src/OmniSharp.Abstractions/Models/Events/EventTypes.cs @@ -10,5 +10,6 @@ public static class EventTypes public const string PackageRestoreStarted = nameof(PackageRestoreStarted); public const string PackageRestoreFinished = nameof(PackageRestoreFinished); public const string UnresolvedDependencies = nameof(UnresolvedDependencies); + public const string ProjectConfiguration = nameof(ProjectConfiguration); } } diff --git a/src/OmniSharp.Abstractions/Models/Events/ProjectConfigurationMessage.cs b/src/OmniSharp.Abstractions/Models/Events/ProjectConfigurationMessage.cs new file mode 100644 index 0000000000..df0707ba78 --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/Events/ProjectConfigurationMessage.cs @@ -0,0 +1,13 @@ +using System.Collections; +using System.Collections.Generic; + +namespace OmniSharp.Models.Events +{ + public class ProjectConfigurationMessage + { + public string ProjectGuid { get; set; } + public IEnumerable TargetFrameworks { get; set; } + public IEnumerable References { get; set; } + public IEnumerable FileExtensions { get; set; } + } +} diff --git a/src/OmniSharp.Abstractions/Models/HashedString.cs b/src/OmniSharp.Abstractions/Models/HashedString.cs new file mode 100644 index 0000000000..263054c5dc --- /dev/null +++ b/src/OmniSharp.Abstractions/Models/HashedString.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OmniSharp.Models +{ + public class HashedString + { + public HashedString(string value) + { + Value = value; + } + + public string Value { get; } + } +} diff --git a/src/OmniSharp.Abstractions/Models/ZeroBasedIndexConverter.cs b/src/OmniSharp.Abstractions/Models/ZeroBasedIndexConverter.cs index da1d16a0b5..4f23528635 100644 --- a/src/OmniSharp.Abstractions/Models/ZeroBasedIndexConverter.cs +++ b/src/OmniSharp.Abstractions/Models/ZeroBasedIndexConverter.cs @@ -5,7 +5,7 @@ namespace OmniSharp.Models { - public class ZeroBasedIndexConverter : JsonConverter + internal class ZeroBasedIndexConverter : JsonConverter { public override bool CanConvert(Type objectType) { diff --git a/src/OmniSharp.Abstractions/OmniSharp.Abstractions.csproj b/src/OmniSharp.Abstractions/OmniSharp.Abstractions.csproj index bdbde24a85..898109ffd8 100644 --- a/src/OmniSharp.Abstractions/OmniSharp.Abstractions.csproj +++ b/src/OmniSharp.Abstractions/OmniSharp.Abstractions.csproj @@ -8,9 +8,6 @@ - - - diff --git a/src/OmniSharp.Plugins/Plugin.cs b/src/OmniSharp.Abstractions/Plugins/Plugin.cs similarity index 100% rename from src/OmniSharp.Plugins/Plugin.cs rename to src/OmniSharp.Abstractions/Plugins/Plugin.cs diff --git a/src/OmniSharp.Plugins/PluginAssemblies.cs b/src/OmniSharp.Abstractions/Plugins/PluginAssemblies.cs similarity index 100% rename from src/OmniSharp.Plugins/PluginAssemblies.cs rename to src/OmniSharp.Abstractions/Plugins/PluginAssemblies.cs diff --git a/src/OmniSharp.Plugins/PluginConfig.cs b/src/OmniSharp.Abstractions/Plugins/PluginConfig.cs similarity index 100% rename from src/OmniSharp.Plugins/PluginConfig.cs rename to src/OmniSharp.Abstractions/Plugins/PluginConfig.cs diff --git a/src/OmniSharp.Plugins/PluginRequest.cs b/src/OmniSharp.Abstractions/Plugins/PluginRequest.cs similarity index 100% rename from src/OmniSharp.Plugins/PluginRequest.cs rename to src/OmniSharp.Abstractions/Plugins/PluginRequest.cs diff --git a/src/OmniSharp.Plugins/PluginResponse.cs b/src/OmniSharp.Abstractions/Plugins/PluginResponse.cs similarity index 100% rename from src/OmniSharp.Plugins/PluginResponse.cs rename to src/OmniSharp.Abstractions/Plugins/PluginResponse.cs diff --git a/src/OmniSharp.Abstractions/Stdio/Services/ISharedTextWriter.cs b/src/OmniSharp.Abstractions/Services/ISharedTextWriter.cs similarity index 82% rename from src/OmniSharp.Abstractions/Stdio/Services/ISharedTextWriter.cs rename to src/OmniSharp.Abstractions/Services/ISharedTextWriter.cs index 6e73a59acc..e4bc56dffc 100644 --- a/src/OmniSharp.Abstractions/Stdio/Services/ISharedTextWriter.cs +++ b/src/OmniSharp.Abstractions/Services/ISharedTextWriter.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace OmniSharp.Stdio.Services +namespace OmniSharp.Services { public interface ISharedTextWriter { diff --git a/src/OmniSharp.Cake/OmniSharp.Cake.csproj b/src/OmniSharp.Cake/OmniSharp.Cake.csproj index 2bd8c4b41b..693df59cc4 100644 --- a/src/OmniSharp.Cake/OmniSharp.Cake.csproj +++ b/src/OmniSharp.Cake/OmniSharp.Cake.csproj @@ -8,6 +8,7 @@ + diff --git a/src/OmniSharp.DotNet/OmniSharp.DotNet.csproj b/src/OmniSharp.DotNet/OmniSharp.DotNet.csproj index 92b1c303a4..5cb6a1084a 100644 --- a/src/OmniSharp.DotNet/OmniSharp.DotNet.csproj +++ b/src/OmniSharp.DotNet/OmniSharp.DotNet.csproj @@ -9,6 +9,7 @@ + diff --git a/src/OmniSharp.Host/CompositionHostBuilder.cs b/src/OmniSharp.Host/CompositionHostBuilder.cs index e3546c9487..a80af3f10a 100644 --- a/src/OmniSharp.Host/CompositionHostBuilder.cs +++ b/src/OmniSharp.Host/CompositionHostBuilder.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OmniSharp.Eventing; +using OmniSharp.FileSystem; using OmniSharp.FileWatching; using OmniSharp.Mef; using OmniSharp.MSBuild.Discovery; @@ -84,7 +85,7 @@ public CompositionHost Build() } var parts = _assemblies - .Concat(new[] { typeof(OmniSharpWorkspace).GetTypeInfo().Assembly, typeof(IRequest).GetTypeInfo().Assembly }) + .Concat(new[] { typeof(OmniSharpWorkspace).GetTypeInfo().Assembly, typeof(IRequest).GetTypeInfo().Assembly, typeof(FileSystemHelper).GetTypeInfo().Assembly }) .Distinct() .SelectMany(a => SafeGetTypes(a)) .ToArray(); @@ -209,6 +210,7 @@ private static bool DependsOnOmniSharp(RuntimeLibrary runtimeLibrary) foreach (var dependency in runtimeLibrary.Dependencies) { if (dependency.Name == "OmniSharp.Abstractions" || + dependency.Name == "OmniSharp.Shared" || dependency.Name == "OmniSharp.Roslyn") { return true; diff --git a/src/OmniSharp.Host/Endpoint/EndpointHandler.cs b/src/OmniSharp.Host/Endpoint/EndpointHandler.cs index cd4cd97dc7..5426649783 100644 --- a/src/OmniSharp.Host/Endpoint/EndpointHandler.cs +++ b/src/OmniSharp.Host/Endpoint/EndpointHandler.cs @@ -14,7 +14,7 @@ using OmniSharp.Models; using OmniSharp.Models.UpdateBuffer; using OmniSharp.Plugins; -using OmniSharp.Stdio.Protocol; +using OmniSharp.Protocol; namespace OmniSharp.Endpoint { diff --git a/src/OmniSharp.Host/Endpoint/GenericEndpointHandler.cs b/src/OmniSharp.Host/Endpoint/GenericEndpointHandler.cs index b73e669a0d..1d0d821939 100644 --- a/src/OmniSharp.Host/Endpoint/GenericEndpointHandler.cs +++ b/src/OmniSharp.Host/Endpoint/GenericEndpointHandler.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using OmniSharp.Stdio.Protocol; +using OmniSharp.Protocol; namespace OmniSharp.Endpoint { @@ -18,4 +18,4 @@ public override Task Handle(RequestPacket context) return _action(context); } } -} \ No newline at end of file +} diff --git a/src/OmniSharp.Host/MSBuild/Discovery/Extensions.cs b/src/OmniSharp.Host/MSBuild/Discovery/Extensions.cs index e58ecadccb..9325d73039 100644 --- a/src/OmniSharp.Host/MSBuild/Discovery/Extensions.cs +++ b/src/OmniSharp.Host/MSBuild/Discovery/Extensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using System.IO; +using Microsoft.Extensions.Logging; namespace OmniSharp.MSBuild.Discovery { @@ -6,34 +7,21 @@ internal static class Extensions { public static void RegisterDefaultInstance(this IMSBuildLocator msbuildLocator, ILogger logger) { - MSBuildInstance instanceToRegister = null; - var invalidVSFound = false; + var bestInstanceFound = GetBestInstance(msbuildLocator, out var invalidVSFound); - foreach (var instance in msbuildLocator.GetInstances()) - { - if (instance.IsInvalidVisualStudio()) - { - invalidVSFound = true; - } - else - { - instanceToRegister = instance; - break; - } - } - - - if (instanceToRegister != null) + if (bestInstanceFound != null) { // Did we end up choosing the standalone MSBuild because there was an invalid Visual Studio? // If so, provide a helpful message to the user. - if (invalidVSFound && instanceToRegister.DiscoveryType == DiscoveryType.StandAlone) + if (invalidVSFound && bestInstanceFound.DiscoveryType == DiscoveryType.StandAlone) { - logger.LogWarning(@"It looks like you have Visual Studio 2017 RTM installed. -Try updating Visual Studio 2017 to the most recent release to enable better MSBuild support."); + logger.LogWarning( + @"It looks like you have Visual Studio 2017 RTM installed. + Try updating Visual Studio 2017 to the most recent release to enable better MSBuild support." + ); } - msbuildLocator.RegisterInstance(instanceToRegister); + msbuildLocator.RegisterInstance(bestInstanceFound); } else { @@ -41,12 +29,68 @@ public static void RegisterDefaultInstance(this IMSBuildLocator msbuildLocator, } } + public static bool HasDotNetSdksResolvers(this MSBuildInstance instance) + { + const string dotnetSdkResolver = "Microsoft.DotNet.MSBuildSdkResolver"; + + return File.Exists( + Path.Combine( + instance.MSBuildPath, + "SdkResolvers", + dotnetSdkResolver, + dotnetSdkResolver + ".dll" + ) + ); + } + /// + /// Checks if it is MSBuild from Visual Studio 2017 RTM that cannot be used. + /// public static bool IsInvalidVisualStudio(this MSBuildInstance instance) - // MSBuild from Visual Studio 2017 RTM cannot be used. => instance.Version.Major == 15 - && instance.Version.Minor == 0 - && (instance.DiscoveryType == DiscoveryType.DeveloperConsole - || instance.DiscoveryType == DiscoveryType.VisualStudioSetup); + && instance.Version.Minor == 0 + && (instance.DiscoveryType == DiscoveryType.DeveloperConsole + || instance.DiscoveryType == DiscoveryType.VisualStudioSetup); + + public static MSBuildInstance GetBestInstance(this IMSBuildLocator msbuildLocator, out bool invalidVSFound) + { + invalidVSFound = false; + MSBuildInstance bestMatchInstance = null; + var bestMatchScore = 0; + + foreach (var instance in msbuildLocator.GetInstances()) + { + var score = GetInstanceFeatureScore(instance); + + invalidVSFound = invalidVSFound || instance.IsInvalidVisualStudio(); + + if (score > bestMatchScore + || (score == bestMatchScore && instance.Version.Major > (bestMatchInstance?.Version.Major ?? 0))) + { + bestMatchInstance = instance; + bestMatchScore = score; + } + } + + return bestMatchInstance; + } + + private static int GetInstanceFeatureScore(MSBuildInstance i) + { + var score = 0; + + if (i.HasDotNetSdksResolvers()) + score++; + + if (i.IsInvalidVisualStudio()) + return int.MinValue; + else + score++; + + if (i.DiscoveryType == DiscoveryType.StandAlone) + score--; + + return score; + } } } diff --git a/src/OmniSharp.Host/OmniSharp.Host.csproj b/src/OmniSharp.Host/OmniSharp.Host.csproj index 01da69face..33bfa336bb 100644 --- a/src/OmniSharp.Host/OmniSharp.Host.csproj +++ b/src/OmniSharp.Host/OmniSharp.Host.csproj @@ -8,8 +8,8 @@ - + diff --git a/src/OmniSharp.Abstractions/Stdio/Protocol/EventPacket.cs b/src/OmniSharp.Host/Protocol/EventPacket.cs similarity index 86% rename from src/OmniSharp.Abstractions/Stdio/Protocol/EventPacket.cs rename to src/OmniSharp.Host/Protocol/EventPacket.cs index 21b482ade9..7c4c83b0ea 100644 --- a/src/OmniSharp.Abstractions/Stdio/Protocol/EventPacket.cs +++ b/src/OmniSharp.Host/Protocol/EventPacket.cs @@ -1,4 +1,4 @@ -namespace OmniSharp.Stdio.Protocol +namespace OmniSharp.Protocol { public class EventPacket : Packet { diff --git a/src/OmniSharp.Abstractions/Stdio/Protocol/Packet.cs b/src/OmniSharp.Host/Protocol/Packet.cs similarity index 92% rename from src/OmniSharp.Abstractions/Stdio/Protocol/Packet.cs rename to src/OmniSharp.Host/Protocol/Packet.cs index 2c7ee3a36d..c9f4227443 100644 --- a/src/OmniSharp.Abstractions/Stdio/Protocol/Packet.cs +++ b/src/OmniSharp.Host/Protocol/Packet.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace OmniSharp.Stdio.Protocol +namespace OmniSharp.Protocol { public class Packet { diff --git a/src/OmniSharp.Abstractions/Stdio/Protocol/RequestPacket.cs b/src/OmniSharp.Host/Protocol/RequestPacket.cs similarity index 97% rename from src/OmniSharp.Abstractions/Stdio/Protocol/RequestPacket.cs rename to src/OmniSharp.Host/Protocol/RequestPacket.cs index 25c62f3d28..967cf1e94d 100644 --- a/src/OmniSharp.Abstractions/Stdio/Protocol/RequestPacket.cs +++ b/src/OmniSharp.Host/Protocol/RequestPacket.cs @@ -3,7 +3,7 @@ using System.Text; using Newtonsoft.Json.Linq; -namespace OmniSharp.Stdio.Protocol +namespace OmniSharp.Protocol { public class RequestPacket : Packet { diff --git a/src/OmniSharp.Abstractions/Stdio/Protocol/ResponsePacket.cs b/src/OmniSharp.Host/Protocol/ResponsePacket.cs similarity index 91% rename from src/OmniSharp.Abstractions/Stdio/Protocol/ResponsePacket.cs rename to src/OmniSharp.Host/Protocol/ResponsePacket.cs index ba8e88dc64..e2ff71a7b0 100644 --- a/src/OmniSharp.Abstractions/Stdio/Protocol/ResponsePacket.cs +++ b/src/OmniSharp.Host/Protocol/ResponsePacket.cs @@ -1,4 +1,4 @@ -namespace OmniSharp.Stdio.Protocol +namespace OmniSharp.Protocol { public class ResponsePacket : Packet { diff --git a/src/OmniSharp.Host/Services/SharedTextWriter.cs b/src/OmniSharp.Host/Services/SharedTextWriter.cs index b661b89548..6be082c519 100644 --- a/src/OmniSharp.Host/Services/SharedTextWriter.cs +++ b/src/OmniSharp.Host/Services/SharedTextWriter.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Threading.Tasks; -using OmniSharp.Stdio.Services; using OmniSharp.Utilities; namespace OmniSharp.Services diff --git a/src/OmniSharp.Http.Driver/app.config b/src/OmniSharp.Http.Driver/app.config index 4e0d1de440..a725124756 100644 --- a/src/OmniSharp.Http.Driver/app.config +++ b/src/OmniSharp.Http.Driver/app.config @@ -7,15 +7,15 @@ - + - + - + diff --git a/src/OmniSharp.Http/Host.cs b/src/OmniSharp.Http/Host.cs index 9fd2b90c13..37233e4aff 100644 --- a/src/OmniSharp.Http/Host.cs +++ b/src/OmniSharp.Http/Host.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using OmniSharp.Eventing; using OmniSharp.Plugins; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; using OmniSharp.Utilities; namespace OmniSharp.Http diff --git a/src/OmniSharp.Http/Middleware/EndpointMiddleware.cs b/src/OmniSharp.Http/Middleware/EndpointMiddleware.cs index c4cb2a72ff..c10974986a 100644 --- a/src/OmniSharp.Http/Middleware/EndpointMiddleware.cs +++ b/src/OmniSharp.Http/Middleware/EndpointMiddleware.cs @@ -12,7 +12,7 @@ using OmniSharp.Models.UpdateBuffer; using OmniSharp.Plugins; using OmniSharp.Services; -using OmniSharp.Stdio.Protocol; +using OmniSharp.Protocol; namespace OmniSharp.Http.Middleware { diff --git a/src/OmniSharp.Http/Startup.cs b/src/OmniSharp.Http/Startup.cs index af496b85c6..0f07e19505 100644 --- a/src/OmniSharp.Http/Startup.cs +++ b/src/OmniSharp.Http/Startup.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Eventing; using OmniSharp.Http.Middleware; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; using OmniSharp.Utilities; namespace OmniSharp.Http diff --git a/src/OmniSharp.LanguageServerProtocol/Eventing/LanguageServerEventEmitter.cs b/src/OmniSharp.LanguageServerProtocol/Eventing/LanguageServerEventEmitter.cs index 8d0231cba2..9b050c7c52 100644 --- a/src/OmniSharp.LanguageServerProtocol/Eventing/LanguageServerEventEmitter.cs +++ b/src/OmniSharp.LanguageServerProtocol/Eventing/LanguageServerEventEmitter.cs @@ -1,23 +1,18 @@ -using System; using System.Linq; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using OmniSharp.Eventing; -using OmniSharp.Extensions.LanguageServer; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.Diagnostics; using OmniSharp.Models.Events; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; +using ILanguageServer = OmniSharp.Extensions.LanguageServer.Server.ILanguageServer; namespace OmniSharp.LanguageServerProtocol.Eventing { public class LanguageServerEventEmitter : IEventEmitter { - private readonly LanguageServer _server; + private ILanguageServer _server; - public LanguageServerEventEmitter(LanguageServer server) + public void SetLanguageServer(ILanguageServer server) { _server = server; } @@ -34,7 +29,7 @@ public void Emit(string kind, object args) foreach (var group in groups) { - _server.PublishDiagnostics(new PublishDiagnosticsParams() + _server.Document.PublishDiagnostics(new PublishDiagnosticsParams() { Uri = group.Key, Diagnostics = group diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/CodeLensHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/CodeLensHandler.cs new file mode 100644 index 0000000000..a1f58e1c74 --- /dev/null +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/CodeLensHandler.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Models; +using OmniSharp.Models.FindUsages; +using OmniSharp.Models.MembersTree; + +namespace OmniSharp.LanguageServerProtocol.Handlers +{ + internal sealed class CodeLensHandler : ICodeLensHandler, ICodeLensResolveHandler + { + public static IEnumerable Enumerate(RequestHandlers handlers) + { + foreach (var (selector, membersAsTreeHandler, findUsagesHandler) in handlers + .OfType< + Mef.IRequestHandler, + Mef.IRequestHandler>()) + { + yield return new CodeLensHandler(membersAsTreeHandler, findUsagesHandler, selector); + } + } + + private CodeLensCapability _capability; + private readonly Mef.IRequestHandler _membersAsTreeHandler; + private readonly Mef.IRequestHandler _findUsagesHandler; + private readonly DocumentSelector _documentSelector; + + public CodeLensHandler( + Mef.IRequestHandler membersAsTreeHandler, + Mef.IRequestHandler findUsagesHandler, + DocumentSelector documentSelector) + { + _membersAsTreeHandler = membersAsTreeHandler; + _findUsagesHandler = findUsagesHandler; + _documentSelector = documentSelector; + } + + public async Task Handle(CodeLensParams request, CancellationToken token) + { + var omnisharpRequest = new MembersTreeRequest() + { + FileName = Helpers.FromUri(request.TextDocument.Uri), + }; + + var omnisharpResponse = await _membersAsTreeHandler.Handle(omnisharpRequest); + var codeLenseContainer = new List(); + + foreach (var node in omnisharpResponse.TopLevelTypeDefinitions) + { + ToCodeLens(request.TextDocument, node, codeLenseContainer); + } + + return codeLenseContainer; + } + + public async Task Handle(CodeLens request, CancellationToken token) + { + var omnisharpRequest = new FindUsagesRequest + { + FileName = Helpers.FromUri(request.Data.ToObject()), + Column = (int) request.Range.Start.Character, + Line = (int) request.Range.Start.Line, + OnlyThisFile = false, + ExcludeDefinition = true + }; + + var omnisharpResponse = await _findUsagesHandler.Handle(omnisharpRequest); + + var length = omnisharpResponse?.QuickFixes?.Count() ?? 0; + + request.Command = new Command + { + Title = length == 1 ? "1 reference" : $"{length} references" + // TODO: Hook up command. + }; + + return request; + } + + + public CodeLensRegistrationOptions GetRegistrationOptions() + { + return new CodeLensRegistrationOptions() + { + DocumentSelector = _documentSelector, + ResolveProvider = true + }; + } + + public void SetCapability(CodeLensCapability capability) + { + _capability = capability; + } + + private static void ToCodeLens(TextDocumentIdentifier textDocument, FileMemberElement node, List codeLensContainer) + { + var codeLens = new CodeLens + { + Data = JToken.FromObject(string.IsNullOrEmpty(node.Location.FileName) ? + textDocument.Uri : + Helpers.ToUri(node.Location.FileName)), + Range = node.Location.ToRange() + }; + + codeLensContainer.Add(codeLens); + + if (node.ChildNodes != null) + { + foreach (var childNode in node.ChildNodes) + { + ToCodeLens(textDocument, childNode, codeLensContainer); + } + } + } + + public bool CanResolve(CodeLens value) + { + var textDocumentUri = value.Data.ToObject(); + + return textDocumentUri != null && + _documentSelector.IsMatch(new TextDocumentAttributes(textDocumentUri, string.Empty)); + } + } +} diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/CompletionHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/CompletionHandler.cs index 37ea8dbfd3..81a589aeeb 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/CompletionHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/CompletionHandler.cs @@ -1,16 +1,12 @@ using System; using System.Collections.Generic; -using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Mef; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.AutoComplete; -using OmniSharp.Models.TypeLookup; namespace OmniSharp.LanguageServerProtocol.Handlers { @@ -75,7 +71,7 @@ public CompletionHandler(Mef.IRequestHandler Handle(TextDocumentPositionParams request, CancellationToken token) + public async Task Handle(CompletionParams request, CancellationToken token) { var omnisharpRequest = new AutoCompleteRequest() { @@ -131,7 +127,8 @@ public CompletionRegistrationOptions GetRegistrationOptions() { return new CompletionRegistrationOptions() { - DocumentSelector = _documentSelector + DocumentSelector = _documentSelector, + TriggerCharacters = new[] { "." }, }; } diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/DefinitionHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/DefinitionHandler.cs index c15dcdeda3..06e103a05a 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/DefinitionHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/DefinitionHandler.cs @@ -1,16 +1,11 @@ using System; using System.Collections.Generic; -using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Mef; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.GotoDefinition; using static OmniSharp.LanguageServerProtocol.Helpers; @@ -43,7 +38,7 @@ public TextDocumentRegistrationOptions GetRegistrationOptions() }; } - public async Task Handle(TextDocumentPositionParams request, CancellationToken token) + public async Task Handle(DefinitionParams request, CancellationToken token) { var omnisharpRequest = new GotoDefinitionRequest() { diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/DocumentSymbolHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/DocumentSymbolHandler.cs index 27689d6041..882b887947 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/DocumentSymbolHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/DocumentSymbolHandler.cs @@ -1,15 +1,11 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Models; -using OmniSharp.Models.MembersFlat; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.MembersTree; -using OmniSharp.Models.TypeLookup; namespace OmniSharp.LanguageServerProtocol.Handlers { @@ -48,7 +44,7 @@ public DocumentSymbolHandler(Mef.IRequestHandler Handle(DocumentSymbolParams request, CancellationToken token) + public async Task Handle(DocumentSymbolParams request, CancellationToken token) { var omnisharpRequest = new MembersTreeRequest() { @@ -56,7 +52,7 @@ public async Task Handle(DocumentSymbolParams reques }; var omnisharpResponse = await _membersAsTreeHandler.Handle(omnisharpRequest); - var symbolInformationContainer = new List(); + var symbolInformationContainer = new List(); foreach (var node in omnisharpResponse.TopLevelTypeDefinitions) { @@ -79,9 +75,9 @@ public void SetCapability(DocumentSymbolCapability capability) _capability = capability; } - private static void ToDocumentSymbol(FileMemberElement node, List symbolInformationContainer, string containerName = null) + private static void ToDocumentSymbol(FileMemberElement node, List symbolInformationContainer, string containerName = null) { - var symbolInformation = new SymbolInformation + var symbolInformation = new DocumentSymbolInformation { Name = node.Location.Text, Kind = Kinds[node.Kind], diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/HoverHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/HoverHandler.cs index e27ae8dc01..77abb4f168 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/HoverHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/HoverHandler.cs @@ -1,14 +1,11 @@ using System; using System.Collections.Generic; -using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Mef; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.TypeLookup; namespace OmniSharp.LanguageServerProtocol.Handlers @@ -41,7 +38,7 @@ public TextDocumentRegistrationOptions GetRegistrationOptions() }; } - public async Task Handle(TextDocumentPositionParams request, CancellationToken token) + public async Task Handle(HoverParams request, CancellationToken token) { var omnisharpRequest = new TypeLookupRequest() { @@ -57,7 +54,7 @@ public async Task Handle(TextDocumentPositionParams request, Cancellation { // TODO: Range? We don't currently have that! // Range = - Contents = new MarkedStringContainer(omnisharpResponse.Type, omnisharpResponse.Documentation) + Contents = new MarkedStringsOrMarkupContent(new MarkedStringContainer(omnisharpResponse.Type, omnisharpResponse.Documentation)) }; } diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/ReferencesHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/ReferencesHandler.cs new file mode 100644 index 0000000000..310fb7df1e --- /dev/null +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/ReferencesHandler.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Models; +using OmniSharp.Models.FindUsages; + +namespace OmniSharp.LanguageServerProtocol.Handlers +{ + internal sealed class ReferencesHandler : IReferencesHandler + { + public static IEnumerable Enumerate(RequestHandlers handlers) + { + foreach (var (selector, handler) in handlers + .OfType>()) + if (handler != null) + yield return new ReferencesHandler(handler, selector); + } + + private ReferencesCapability _capability; + private readonly Mef.IRequestHandler _findUsagesHandler; + private readonly DocumentSelector _documentSelector; + + public ReferencesHandler(Mef.IRequestHandler findUsagesHandler, DocumentSelector documentSelector) + { + _findUsagesHandler = findUsagesHandler; + _documentSelector = documentSelector; + } + + public async Task Handle(ReferenceParams request, CancellationToken token) + { + var omnisharpRequest = new FindUsagesRequest + { + FileName = Helpers.FromUri(request.TextDocument.Uri), + Column = Convert.ToInt32(request.Position.Character), + Line = Convert.ToInt32(request.Position.Line), + OnlyThisFile = false, + ExcludeDefinition = !request.Context.IncludeDeclaration + }; + + var omnisharpResponse = await _findUsagesHandler.Handle(omnisharpRequest); + + return omnisharpResponse.QuickFixes?.Select(x => new Location + { + Uri = Helpers.ToUri(x.FileName), + Range = x.ToRange() + }).ToArray(); + } + + public TextDocumentRegistrationOptions GetRegistrationOptions() + { + return new TextDocumentRegistrationOptions() + { + DocumentSelector = _documentSelector + }; + } + + public void SetCapability(ReferencesCapability capability) + { + _capability = capability; + } + } +} diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/RenameHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/RenameHandler.cs index 698484f43e..431884db98 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/RenameHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/RenameHandler.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.Rename; namespace OmniSharp.LanguageServerProtocol.Handlers @@ -74,9 +73,9 @@ public async Task Handle(RenameParams request, CancellationToken }; } - public TextDocumentRegistrationOptions GetRegistrationOptions() + public RenameRegistrationOptions GetRegistrationOptions() { - return new TextDocumentRegistrationOptions + return new RenameRegistrationOptions { DocumentSelector = _documentSelector }; diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/SignatureHelpHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/SignatureHelpHandler.cs index 5f1ce5aaf4..0382118165 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/SignatureHelpHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/SignatureHelpHandler.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; -using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Models.AutoComplete; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Models.SignatureHelp; namespace OmniSharp.LanguageServerProtocol.Handlers @@ -33,7 +31,7 @@ public static IEnumerable Enumerate(RequestHandlers handlers) yield return new SignatureHelpHandler(handler, selector); } - public async Task Handle(TextDocumentPositionParams request, CancellationToken token) + public async Task Handle(SignatureHelpParams request, CancellationToken token) { var omnisharpRequest = new SignatureHelpRequest { diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/TextDocumentSyncHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/TextDocumentSyncHandler.cs index ca2b569c3c..5669e3082c 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/TextDocumentSyncHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/TextDocumentSyncHandler.cs @@ -4,23 +4,21 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using OmniSharp.Extensions.Embedded.MediatR; using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.LanguageServer.Abstractions; -using OmniSharp.Extensions.LanguageServer.Capabilities.Client; -using OmniSharp.Extensions.LanguageServer.Capabilities.Server; -using OmniSharp.Extensions.LanguageServer.Models; using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Extensions.LanguageServer.Protocol.Document; -using OmniSharp.Mef; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities; using OmniSharp.Models; using OmniSharp.Models.FileClose; using OmniSharp.Models.FileOpen; using OmniSharp.Models.UpdateBuffer; -using OmniSharp.Roslyn; namespace OmniSharp.LanguageServerProtocol.Handlers { - class TextDocumentSyncHandler : ITextDocumentSyncHandler, IWillSaveTextDocumentHandler, IWillSaveWaitUntilTextDocumentHandler + class TextDocumentSyncHandler : ITextDocumentSyncHandler { public static IEnumerable Enumerate( RequestHandlers handlers, @@ -34,7 +32,7 @@ public static IEnumerable Enumerate( { // TODO: Fix once cake has working support for incremental var documentSyncKind = TextDocumentSyncKind.Incremental; - // if (selector.ToString().IndexOf(".cake") > -1) documentSyncKind = TextDocumentSyncKind.Full; + if (selector.ToString().IndexOf(".cake") > -1) documentSyncKind = TextDocumentSyncKind.Full; yield return new TextDocumentSyncHandler(openHandler, closeHandler, bufferHandler, selector, documentSyncKind, workspace); } } @@ -60,20 +58,10 @@ public TextDocumentSyncHandler( _bufferHandler = bufferHandler; _workspace = workspace; _documentSelector = documentSelector; - Options.Change = documentSyncKind; + Change = documentSyncKind; } - public TextDocumentSyncOptions Options { get; } = new TextDocumentSyncOptions() - { - Change = TextDocumentSyncKind.Incremental, - OpenClose = true, - WillSave = false, // Do we need to configure this? - WillSaveWaitUntil = false, // Do we need to configure this? - Save = new SaveOptions() - { - IncludeText = true - } - }; + public TextDocumentSyncKind Change { get; } public TextDocumentAttributes GetTextDocumentAttributes(Uri uri) { @@ -82,17 +70,19 @@ public TextDocumentAttributes GetTextDocumentAttributes(Uri uri) return new TextDocumentAttributes(uri, ""); } - public Task Handle(DidChangeTextDocumentParams notification) + public async Task Handle(DidChangeTextDocumentParams notification, CancellationToken cancellationToken) { var contentChanges = notification.ContentChanges.ToArray(); if (contentChanges.Length == 1 && contentChanges[0].Range == null) { var change = contentChanges[0]; - return _bufferHandler.Handle(new UpdateBufferRequest() + await _bufferHandler.Handle(new UpdateBufferRequest() { FileName = Helpers.FromUri(notification.TextDocument.Uri), Buffer = change.Text }); + + return Unit.Value; } var changes = contentChanges @@ -106,60 +96,53 @@ public Task Handle(DidChangeTextDocumentParams notification) }) .ToArray(); - return _bufferHandler.Handle(new UpdateBufferRequest() + await _bufferHandler.Handle(new UpdateBufferRequest() { FileName = Helpers.FromUri(notification.TextDocument.Uri), Changes = changes }); + + return Unit.Value; } - public Task Handle(DidOpenTextDocumentParams notification) + public async Task Handle(DidOpenTextDocumentParams notification, CancellationToken cancellationToken) { - return _openHandler?.Handle(new FileOpenRequest() + if (_openHandler != null) { - Buffer = notification.TextDocument.Text, - FileName = Helpers.FromUri(notification.TextDocument.Uri) - }) ?? Task.CompletedTask; + await _openHandler.Handle(new FileOpenRequest() + { + Buffer = notification.TextDocument.Text, + FileName = Helpers.FromUri(notification.TextDocument.Uri) + }); + } + + return Unit.Value; } - public Task Handle(DidCloseTextDocumentParams notification) + public async Task Handle(DidCloseTextDocumentParams notification, CancellationToken cancellationToken) { - return _closeHandler?.Handle(new FileCloseRequest() + if (_closeHandler != null) { - FileName = Helpers.FromUri(notification.TextDocument.Uri) - }) ?? Task.CompletedTask; + await _closeHandler.Handle(new FileCloseRequest() + { + FileName = Helpers.FromUri(notification.TextDocument.Uri) + }); + } + + return Unit.Value; } - public Task Handle(DidSaveTextDocumentParams notification) + public async Task Handle(DidSaveTextDocumentParams notification, CancellationToken cancellationToken) { if (_capability?.DidSave == true) { - return _bufferHandler.Handle(new UpdateBufferRequest() + await _bufferHandler.Handle(new UpdateBufferRequest() { FileName = Helpers.FromUri(notification.TextDocument.Uri), Buffer = notification.Text }); } - return Task.CompletedTask; - } - - public Task Handle(WillSaveTextDocumentParams notification) - { - // TODO: Do we have a need for this? - if (_capability?.WillSave == true) { } - return Task.CompletedTask; - } - - public Task Handle(WillSaveTextDocumentParams request, CancellationToken token) - { - // TODO: Do we have a need for this? - if (_capability?.WillSaveWaitUntil == true) { } - return Task.CompletedTask; - } - - public void SetCapability(SynchronizationCapability capability) - { - _capability = capability; + return Unit.Value; } TextDocumentChangeRegistrationOptions IRegistration.GetRegistrationOptions() @@ -167,7 +150,7 @@ TextDocumentChangeRegistrationOptions IRegistration handlers) - { - foreach (var handler in handlers) - langaugeServer.AddHandler(handler); - return langaugeServer; - } - } -} \ No newline at end of file diff --git a/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs b/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs index 053e027744..9402f16120 100644 --- a/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs +++ b/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs @@ -3,18 +3,20 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Reactive; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OmniSharp.Extensions.LanguageServer; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Extensions.LanguageServer.Server; using OmniSharp.LanguageServerProtocol.Eventing; using OmniSharp.LanguageServerProtocol.Handlers; using OmniSharp.Mef; using OmniSharp.Models.Diagnostics; +using OmniSharp.Roslyn; using OmniSharp.Services; using OmniSharp.Utilities; @@ -22,12 +24,13 @@ namespace OmniSharp.LanguageServerProtocol { internal class LanguageServerHost : IDisposable { - private readonly ServiceCollection _services; - private readonly LanguageServer _server; - private CompositionHost _compositionHost; - private readonly LanguageServerLoggerFactory _loggerFactory; + private readonly LanguageServerOptions _options; + private IServiceCollection _services; + private readonly LoggerFactory _loggerFactory; private readonly CommandLineApplication _application; private readonly CancellationTokenSource _cancellationTokenSource; + private CompositionHost _compositionHost; + private LanguageServerEventEmitter _eventEmitter; private IServiceProvider _serviceProvider; private RequestHandlers _handlers; private OmniSharpEnvironment _environment; @@ -39,11 +42,16 @@ public LanguageServerHost( CommandLineApplication application, CancellationTokenSource cancellationTokenSource) { - _services = new ServiceCollection(); - _loggerFactory = new LanguageServerLoggerFactory(); - _services.AddSingleton(_loggerFactory); - _server = new LanguageServer(input, output, _loggerFactory); - _server.OnInitialize(Initialize); + _loggerFactory = new LoggerFactory(); + _logger = _loggerFactory.CreateLogger(); + _options = new LanguageServerOptions() + .WithInput(input) + .WithOutput(output) + .WithLoggerFactory(_loggerFactory) + .AddDefaultLoggingProvider() + .OnInitialize(Initialize) + .WithMinimumLogLevel(application.LogLevel) + .WithServices(services => _services = services); _application = application; _cancellationTokenSource = cancellationTokenSource; } @@ -59,13 +67,13 @@ private static LogLevel GetLogLevel(InitializeTrace initializeTrace) { switch (initializeTrace) { - case InitializeTrace.verbose: + case InitializeTrace.Verbose: return LogLevel.Trace; - case InitializeTrace.off: + case InitializeTrace.Off: return LogLevel.Warning; - case InitializeTrace.messages: + case InitializeTrace.Messages: default: return LogLevel.Information; } @@ -79,14 +87,9 @@ private void CreateCompositionHost(InitializeParams initializeParams) GetLogLevel(initializeParams.Trace), _application.OtherArgs.ToArray()); - // TODO: Make this work with logger factory differently - // Maybe create a child logger factory? - _loggerFactory.AddProvider(_server, _environment); - _logger = _loggerFactory.CreateLogger(); - var configurationRoot = new ConfigurationBuilder(_environment).Build(); - var eventEmitter = new LanguageServerEventEmitter(_server); - _serviceProvider = CompositionHostBuilder.CreateDefaultServiceProvider(_environment, configurationRoot, eventEmitter, _services); + _eventEmitter = new LanguageServerEventEmitter(); + _serviceProvider = CompositionHostBuilder.CreateDefaultServiceProvider(_environment, configurationRoot, _eventEmitter, _services); var plugins = _application.CreatePluginAssemblies(); @@ -142,57 +145,46 @@ private void CreateCompositionHost(InitializeParams initializeParams) _logger.LogTrace("--- Handler Definitions ---"); } - private Task Initialize(InitializeParams initializeParams) + private Task Initialize(Extensions.LanguageServer.Server.ILanguageServer server, InitializeParams initializeParams) { CreateCompositionHost(initializeParams); // TODO: Make it easier to resolve handlers from MEF (without having to add more attributes to the services if we can help it) var workspace = _compositionHost.GetExport(); - - _server.AddHandlers(TextDocumentSyncHandler.Enumerate(_handlers, workspace)); - _server.AddHandlers(DefinitionHandler.Enumerate(_handlers)); - _server.AddHandlers(HoverHandler.Enumerate(_handlers)); - _server.AddHandlers(CompletionHandler.Enumerate(_handlers)); - _server.AddHandlers(SignatureHelpHandler.Enumerate(_handlers)); - _server.AddHandlers(RenameHandler.Enumerate(_handlers)); - _server.AddHandlers(DocumentSymbolHandler.Enumerate(_handlers)); - - _server.LogMessage(new LogMessageParams() + _compositionHost.GetExport().IsEnabled = true; + + foreach (var handler in TextDocumentSyncHandler.Enumerate(_handlers, workspace) + .Concat(DefinitionHandler.Enumerate(_handlers)) + .Concat(HoverHandler.Enumerate(_handlers)) + .Concat(CompletionHandler.Enumerate(_handlers)) + .Concat(SignatureHelpHandler.Enumerate(_handlers)) + .Concat(RenameHandler.Enumerate(_handlers)) + .Concat(DocumentSymbolHandler.Enumerate(_handlers)) + .Concat(ReferencesHandler.Enumerate(_handlers)) + .Concat(CodeLensHandler.Enumerate(_handlers))) { - Message = "Added handlers... waiting for initialize...", - Type = MessageType.Log - }); + server.AddHandlers(handler); + } return Task.CompletedTask; } public async Task Start() { - _server.LogMessage(new LogMessageParams() - { - Message = "Starting server...", - Type = MessageType.Log - }); + var server = await LanguageServer.From(_options); + server.Exit.Subscribe(Observer.Create(i => _cancellationTokenSource.Cancel())); - await _server.Initialize(); + _eventEmitter.SetLanguageServer(server); - _server.LogMessage(new LogMessageParams() + server.Window.LogMessage(new LogMessageParams() { Message = "initialized...", Type = MessageType.Log }); - var logger = _loggerFactory.CreateLogger(typeof(LanguageServerHost)); WorkspaceInitializer.Initialize(_serviceProvider, _compositionHost); - // Kick on diagnostics - var diagnosticHandler = _handlers.GetAll() - .OfType>(); - - foreach (var handler in diagnosticHandler) - await handler.Handle(new DiagnosticsRequest()); - - logger.LogInformation($"Omnisharp server running using Lsp at location '{_environment.TargetDirectory}' on host {_environment.HostProcessId}."); + _logger.LogInformation($"Omnisharp server running using Lsp at location '{_environment.TargetDirectory}' on host {_environment.HostProcessId}."); Console.CancelKeyPress += (sender, e) => { diff --git a/src/OmniSharp.LanguageServerProtocol/LanguageServerLoggerFactory.cs b/src/OmniSharp.LanguageServerProtocol/LanguageServerLoggerFactory.cs deleted file mode 100644 index 1524340178..0000000000 --- a/src/OmniSharp.LanguageServerProtocol/LanguageServerLoggerFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using OmniSharp.Extensions.LanguageServer; -using OmniSharp.LanguageServerProtocol.Logging; -using OmniSharp.Roslyn; -using OmniSharp.Services; - -namespace OmniSharp.LanguageServerProtocol -{ - class LanguageServerLoggerFactory : ILoggerFactory - { - private readonly LanguageServerLoggerProvider _provider; - - public LanguageServerLoggerFactory() - { - _provider = new LanguageServerLoggerProvider(); - } - public void AddProvider(ILoggerProvider provider) { } - public void AddProvider(LanguageServer server, OmniSharpEnvironment environment) - { - if (environment.LogLevel <= LogLevel.Debug) - _provider.SetProvider(server, (category, level) => true); - else - _provider.SetProvider(server, (category, level) => LogFilter(category, level, environment)); - } - - public ILogger CreateLogger(string categoryName) - { - return _provider?.CreateLogger(categoryName) ?? NullLogger.Instance; - } - - public void Dispose() - { - throw new NotImplementedException(); - } - - private static bool LogFilter(string category, LogLevel level, IOmniSharpEnvironment environment) - { - if (environment.LogLevel > level) - { - return false; - } - - if (!category.StartsWith("OmniSharp", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (string.Equals(category, typeof(WorkspaceInformationService).FullName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (string.Equals(category, typeof(ProjectEventForwarder).FullName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - } -} diff --git a/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLogger.cs b/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLogger.cs deleted file mode 100644 index 9160d5ef2b..0000000000 --- a/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLogger.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using OmniSharp.Extensions.LanguageServer; -using OmniSharp.Extensions.LanguageServer.Models; -using OmniSharp.Extensions.LanguageServer.Protocol; -using OmniSharp.Logging; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; -using OmniSharp.Utilities; - -namespace OmniSharp.LanguageServerProtocol.Logging -{ - class LanguageServerLogger : BaseLogger - { - private readonly LanguageServerLoggerProvider _provider; - - public LanguageServerLogger(LanguageServerLoggerProvider provider, string categoryName, bool addHeader = true) : base(categoryName, provider._filter, addHeader) - { - _provider = provider; - } - - protected override void WriteMessage(LogLevel logLevel, string message) - { - if (_provider._server == null) return; - var messageType = GetMessageType(logLevel); - if (messageType.HasValue) - { - _provider._server.LogMessage(new LogMessageParams() - { - Type = messageType.Value, - Message = message - }); - } - } - - private MessageType? GetMessageType(LogLevel level) - { - switch (level) - { - case LogLevel.Critical: - case LogLevel.Error: - return MessageType.Error; - - case LogLevel.Warning: - return MessageType.Warning; - - case LogLevel.Information: - return MessageType.Info; - - case LogLevel.Debug: - case LogLevel.Trace: - return MessageType.Log; - } - return null; - } - } -} diff --git a/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLoggerProvider.cs b/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLoggerProvider.cs deleted file mode 100644 index aa3211f793..0000000000 --- a/src/OmniSharp.LanguageServerProtocol/Logging/LanguageServerLoggerProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using OmniSharp.Extensions.LanguageServer; -using OmniSharp.Stdio.Services; - -namespace OmniSharp.LanguageServerProtocol.Logging -{ - class LanguageServerLoggerProvider : ILoggerProvider - { - internal LanguageServer _server { get; private set; } - internal Func _filter { get; private set; } - - public LanguageServerLoggerProvider() { } - public void SetProvider(LanguageServer server, Func filter) - { - _server = server; - _filter = filter; - } - - public ILogger CreateLogger(string name) - { - return new LanguageServerLogger(this, name); - } - public void Dispose() { } - } -} diff --git a/src/OmniSharp.LanguageServerProtocol/OmniSharp.LanguageServerProtocol.csproj b/src/OmniSharp.LanguageServerProtocol/OmniSharp.LanguageServerProtocol.csproj index b7224f14ce..5874c16a7e 100644 --- a/src/OmniSharp.LanguageServerProtocol/OmniSharp.LanguageServerProtocol.csproj +++ b/src/OmniSharp.LanguageServerProtocol/OmniSharp.LanguageServerProtocol.csproj @@ -9,8 +9,10 @@ - - + + + + diff --git a/src/OmniSharp.LanguageServerProtocol/RequestHandlerCollection.cs b/src/OmniSharp.LanguageServerProtocol/RequestHandlerCollection.cs index 879eee3991..48e24ff35c 100644 --- a/src/OmniSharp.LanguageServerProtocol/RequestHandlerCollection.cs +++ b/src/OmniSharp.LanguageServerProtocol/RequestHandlerCollection.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Collections.Generic; -using OmniSharp.Extensions.LanguageServer.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Mef; namespace OmniSharp.LanguageServerProtocol @@ -29,4 +29,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/src/OmniSharp.LanguageServerProtocol/RequestHandlers.cs b/src/OmniSharp.LanguageServerProtocol/RequestHandlers.cs index ad083eee02..8348ff46eb 100644 --- a/src/OmniSharp.LanguageServerProtocol/RequestHandlers.cs +++ b/src/OmniSharp.LanguageServerProtocol/RequestHandlers.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using OmniSharp.Extensions.LanguageServer.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Mef; namespace OmniSharp.LanguageServerProtocol diff --git a/src/OmniSharp.MSBuild/HashingAlgorithm.cs b/src/OmniSharp.MSBuild/HashingAlgorithm.cs new file mode 100644 index 0000000000..05d097a67f --- /dev/null +++ b/src/OmniSharp.MSBuild/HashingAlgorithm.cs @@ -0,0 +1,13 @@ +using OmniSharp.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace OmniSharp.MSBuild +{ + public interface IHasher + { + HashedString HashInput(string clearText); + } +} diff --git a/src/OmniSharp.MSBuild/Notification/ProjectLoadedEventArgs.cs b/src/OmniSharp.MSBuild/Notification/ProjectLoadedEventArgs.cs index 9f166898c0..2e28794638 100644 --- a/src/OmniSharp.MSBuild/Notification/ProjectLoadedEventArgs.cs +++ b/src/OmniSharp.MSBuild/Notification/ProjectLoadedEventArgs.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using OmniSharp.MSBuild.Logging; using MSB = Microsoft.Build; @@ -11,17 +12,26 @@ public class ProjectLoadedEventArgs public MSB.Execution.ProjectInstance ProjectInstance { get; } public ImmutableArray Diagnostics { get; } public bool IsReload { get; } + public IEnumerable References { get; } + public ImmutableArray SourceFiles { get; } + public bool ProjectIdIsDefinedInSolution { get; } public ProjectLoadedEventArgs( ProjectId id, MSB.Execution.ProjectInstance projectInstance, ImmutableArray diagnostics, - bool isReload) + bool isReload, + bool projectIdIsDefinedInSolution, + ImmutableArray sourceFiles, + IEnumerable references = null) { Id = id; ProjectInstance = projectInstance; Diagnostics = diagnostics; IsReload = isReload; + ProjectIdIsDefinedInSolution = projectIdIsDefinedInSolution; + References = references; + SourceFiles = sourceFiles; } } } diff --git a/src/OmniSharp.MSBuild/ProjectFile/MetadataNames.cs b/src/OmniSharp.MSBuild/ProjectFile/MetadataNames.cs index 74c5835fd8..56e54025e4 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/MetadataNames.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/MetadataNames.cs @@ -8,5 +8,6 @@ internal static class MetadataNames public const string OriginalItemSpec = nameof(OriginalItemSpec); public const string ReferenceSourceTarget = nameof(ReferenceSourceTarget); public const string Version = nameof(Version); + public const string Aliases = nameof(Aliases); } } diff --git a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.ProjectData.cs b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.ProjectData.cs index efa43ca284..52e834ac23 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.ProjectData.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.ProjectData.cs @@ -32,6 +32,7 @@ private class ProjectData public OutputKind OutputKind { get; } public LanguageVersion LanguageVersion { get; } + public NullableContextOptions NullableContextOptions { get; } public bool AllowUnsafeCode { get; } public string DocumentationFile { get; } public ImmutableArray PreprocessorSymbolNames { get; } @@ -45,6 +46,7 @@ private class ProjectData public ImmutableArray References { get; } public ImmutableArray PackageReferences { get; } public ImmutableArray Analyzers { get; } + public ImmutableDictionary ReferenceAliases { get; } private ProjectData() { @@ -58,6 +60,7 @@ private ProjectData() References = ImmutableArray.Empty; PackageReferences = ImmutableArray.Empty; Analyzers = ImmutableArray.Empty; + ReferenceAliases = ImmutableDictionary.Empty; } private ProjectData( @@ -69,6 +72,7 @@ private ProjectData( ImmutableArray targetFrameworks, OutputKind outputKind, LanguageVersion languageVersion, + NullableContextOptions nullableContextOptions, bool allowUnsafeCode, string documentationFile, ImmutableArray preprocessorSymbolNames, @@ -93,6 +97,7 @@ private ProjectData( OutputKind = outputKind; LanguageVersion = languageVersion; + NullableContextOptions = nullableContextOptions; AllowUnsafeCode = allowUnsafeCode; DocumentationFile = documentationFile; PreprocessorSymbolNames = preprocessorSymbolNames.EmptyIfDefault(); @@ -111,6 +116,7 @@ private ProjectData( ImmutableArray targetFrameworks, OutputKind outputKind, LanguageVersion languageVersion, + NullableContextOptions nullableContextOptions, bool allowUnsafeCode, string documentationFile, ImmutableArray preprocessorSymbolNames, @@ -121,9 +127,10 @@ private ProjectData( ImmutableArray projectReferences, ImmutableArray references, ImmutableArray packageReferences, - ImmutableArray analyzers) + ImmutableArray analyzers, + ImmutableDictionary referenceAliases) : this(guid, name, assemblyName, targetPath, outputPath, intermediateOutputPath, projectAssetsFile, - configuration, platform, targetFramework, targetFrameworks, outputKind, languageVersion, allowUnsafeCode, + configuration, platform, targetFramework, targetFrameworks, outputKind, languageVersion, nullableContextOptions, allowUnsafeCode, documentationFile, preprocessorSymbolNames, suppressedDiagnosticIds, signAssembly, assemblyOriginatorKeyFile) { SourceFiles = sourceFiles.EmptyIfDefault(); @@ -131,6 +138,7 @@ private ProjectData( References = references.EmptyIfDefault(); PackageReferences = packageReferences.EmptyIfDefault(); Analyzers = analyzers.EmptyIfDefault(); + ReferenceAliases = referenceAliases; } public static ProjectData Create(MSB.Evaluation.Project project) @@ -158,6 +166,7 @@ public static ProjectData Create(MSB.Evaluation.Project project) var languageVersion = PropertyConverter.ToLanguageVersion(project.GetPropertyValue(PropertyNames.LangVersion)); var allowUnsafeCode = PropertyConverter.ToBoolean(project.GetPropertyValue(PropertyNames.AllowUnsafeBlocks), defaultValue: false); var outputKind = PropertyConverter.ToOutputKind(project.GetPropertyValue(PropertyNames.OutputType)); + var nullableContextOptions = PropertyConverter.ToNullableContextOptions(project.GetPropertyValue(PropertyNames.NullableContextOptions)); var documentationFile = project.GetPropertyValue(PropertyNames.DocumentationFile); var preprocessorSymbolNames = PropertyConverter.ToPreprocessorSymbolNames(project.GetPropertyValue(PropertyNames.DefineConstants)); var suppressedDiagnosticIds = PropertyConverter.ToSuppressedDiagnosticIds(project.GetPropertyValue(PropertyNames.NoWarn)); @@ -166,7 +175,7 @@ public static ProjectData Create(MSB.Evaluation.Project project) return new ProjectData( guid, name, assemblyName, targetPath, outputPath, intermediateOutputPath, projectAssetsFile, - configuration, platform, targetFramework, targetFrameworks, outputKind, languageVersion, allowUnsafeCode, + configuration, platform, targetFramework, targetFrameworks, outputKind, languageVersion, nullableContextOptions, allowUnsafeCode, documentationFile, preprocessorSymbolNames, suppressedDiagnosticIds, signAssembly, assemblyOriginatorKeyFile); } @@ -195,6 +204,7 @@ public static ProjectData Create(MSB.Execution.ProjectInstance projectInstance) var languageVersion = PropertyConverter.ToLanguageVersion(projectInstance.GetPropertyValue(PropertyNames.LangVersion)); var allowUnsafeCode = PropertyConverter.ToBoolean(projectInstance.GetPropertyValue(PropertyNames.AllowUnsafeBlocks), defaultValue: false); var outputKind = PropertyConverter.ToOutputKind(projectInstance.GetPropertyValue(PropertyNames.OutputType)); + var nullableContextOptions = PropertyConverter.ToNullableContextOptions(projectInstance.GetPropertyValue(PropertyNames.NullableContextOptions)); var documentationFile = projectInstance.GetPropertyValue(PropertyNames.DocumentationFile); var preprocessorSymbolNames = PropertyConverter.ToPreprocessorSymbolNames(projectInstance.GetPropertyValue(PropertyNames.DefineConstants)); var suppressedDiagnosticIds = PropertyConverter.ToSuppressedDiagnosticIds(projectInstance.GetPropertyValue(PropertyNames.NoWarn)); @@ -208,6 +218,7 @@ public static ProjectData Create(MSB.Execution.ProjectInstance projectInstance) projectInstance.GetItems(ItemNames.ProjectReference), filter: IsCSharpProject); var references = ImmutableArray.CreateBuilder(); + var referenceAliases = ImmutableDictionary.CreateBuilder(); foreach (var referencePathItem in projectInstance.GetItems(ItemNames.ReferencePath)) { var referenceSourceTarget = referencePathItem.GetMetadataValue(MetadataNames.ReferenceSourceTarget); @@ -233,17 +244,22 @@ public static ProjectData Create(MSB.Execution.ProjectInstance projectInstance) { references.Add(fullPath); } + var aliases = referencePathItem.GetMetadataValue(MetadataNames.Aliases); + if(!string.IsNullOrEmpty(aliases)) + { + referenceAliases[referencePathItem.EvaluatedInclude] = aliases; + } } - + var packageReferences = GetPackageReferences(projectInstance.GetItems(ItemNames.PackageReference)); var analyzers = GetFullPaths(projectInstance.GetItems(ItemNames.Analyzer)); return new ProjectData(guid, name, assemblyName, targetPath, outputPath, intermediateOutputPath, projectAssetsFile, configuration, platform, targetFramework, targetFrameworks, - outputKind, languageVersion, allowUnsafeCode, documentationFile, preprocessorSymbolNames, suppressedDiagnosticIds, + outputKind, languageVersion, nullableContextOptions, allowUnsafeCode, documentationFile, preprocessorSymbolNames, suppressedDiagnosticIds, signAssembly, assemblyOriginatorKeyFile, - sourceFiles, projectReferences, references.ToImmutable(), packageReferences, analyzers); + sourceFiles, projectReferences, references.ToImmutable(), packageReferences, analyzers, referenceAliases.ToImmutableDictionary()); } private static bool IsCSharpProject(string filePath) diff --git a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs index dc7d484a78..17f6a2f6a5 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs @@ -17,7 +17,7 @@ internal partial class ProjectFileInfo public string FilePath { get; } public string Directory { get; } - public ProjectId Id { get; } + public ProjectId Id { get => ProjectIdInfo.Id; } public Guid Guid => _data.Guid; public string Name => _data.Name; @@ -35,6 +35,7 @@ internal partial class ProjectFileInfo public OutputKind OutputKind => _data.OutputKind; public LanguageVersion LanguageVersion => _data.LanguageVersion; + public NullableContextOptions NullableContextOptions => _data.NullableContextOptions; public bool AllowUnsafeCode => _data.AllowUnsafeCode; public string DocumentationFile => _data.DocumentationFile; public ImmutableArray PreprocessorSymbolNames => _data.PreprocessorSymbolNames; @@ -48,13 +49,16 @@ internal partial class ProjectFileInfo public ImmutableArray ProjectReferences => _data.ProjectReferences; public ImmutableArray PackageReferences => _data.PackageReferences; public ImmutableArray Analyzers => _data.Analyzers; + public ImmutableDictionary ReferenceAliases => _data.ReferenceAliases; + + public ProjectIdInfo ProjectIdInfo { get; } private ProjectFileInfo( - ProjectId id, + ProjectIdInfo projectIdInfo, string filePath, ProjectData data) { - this.Id = id; + this.ProjectIdInfo = projectIdInfo; this.FilePath = filePath; this.Directory = Path.GetDirectoryName(filePath); @@ -65,7 +69,7 @@ internal static ProjectFileInfo CreateEmpty(string filePath) { var id = ProjectId.CreateNewId(debugName: filePath); - return new ProjectFileInfo(id, filePath, data: null); + return new ProjectFileInfo(new ProjectIdInfo(id, isDefinedInSolution:false), filePath, data: null); } internal static ProjectFileInfo CreateNoBuild(string filePath, ProjectLoader loader) @@ -73,11 +77,13 @@ internal static ProjectFileInfo CreateNoBuild(string filePath, ProjectLoader loa var id = ProjectId.CreateNewId(debugName: filePath); var project = loader.EvaluateProjectFile(filePath); var data = ProjectData.Create(project); + //we are not reading the solution here + var projectIdInfo = new ProjectIdInfo(id, isDefinedInSolution: false); - return new ProjectFileInfo(id, filePath, data); + return new ProjectFileInfo(projectIdInfo, filePath, data); } - public static (ProjectFileInfo, ImmutableArray, ProjectLoadedEventArgs) Load(string filePath, ProjectLoader loader) + public static (ProjectFileInfo, ImmutableArray, ProjectLoadedEventArgs) Load(string filePath, ProjectIdInfo projectIdInfo, ProjectLoader loader) { if (!File.Exists(filePath)) { @@ -90,10 +96,15 @@ public static (ProjectFileInfo, ImmutableArray, ProjectLoaded return (null, diagnostics, null); } - var id = ProjectId.CreateNewId(debugName: filePath); var data = ProjectData.Create(projectInstance); - var projectFileInfo = new ProjectFileInfo(id, filePath, data); - var eventArgs = new ProjectLoadedEventArgs(id, projectInstance, diagnostics, isReload: false); + var projectFileInfo = new ProjectFileInfo(projectIdInfo, filePath, data); + var eventArgs = new ProjectLoadedEventArgs(projectIdInfo.Id, + projectInstance, + diagnostics, + isReload: false, + projectIdInfo.IsDefinedInSolution, + projectFileInfo.SourceFiles, + data.References); return (projectFileInfo, diagnostics, eventArgs); } @@ -107,8 +118,8 @@ public static (ProjectFileInfo, ImmutableArray, ProjectLoaded } var data = ProjectData.Create(projectInstance); - var projectFileInfo = new ProjectFileInfo(Id, FilePath, data); - var eventArgs = new ProjectLoadedEventArgs(Id, projectInstance, diagnostics, isReload: true); + var projectFileInfo = new ProjectFileInfo(ProjectIdInfo, FilePath, data); + var eventArgs = new ProjectLoadedEventArgs(Id, projectInstance, diagnostics, isReload: true, ProjectIdInfo.IsDefinedInSolution,data.References); return (projectFileInfo, diagnostics, eventArgs); } diff --git a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfoExtensions.cs b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfoExtensions.cs index 7ad5386fa0..c6fec99f89 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfoExtensions.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfoExtensions.cs @@ -18,6 +18,11 @@ public static CSharpCompilationOptions CreateCompilationOptions(this ProjectFile result = result.WithAllowUnsafe(true); } + if (projectFileInfo.NullableContextOptions != result.NullableContextOptions) + { + result = result.WithNullableContextOptions(projectFileInfo.NullableContextOptions); + } + result = result.WithSpecificDiagnosticOptions(CompilationOptionsHelper.GetDefaultSuppressedDiagnosticOptions(projectFileInfo.SuppressedDiagnosticIds)); if (projectFileInfo.SignAssembly && !string.IsNullOrEmpty(projectFileInfo.AssemblyOriginatorKeyFile)) diff --git a/src/OmniSharp.MSBuild/ProjectFile/PropertyConverter.cs b/src/OmniSharp.MSBuild/ProjectFile/PropertyConverter.cs index f095687cfa..efe7379bd4 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/PropertyConverter.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/PropertyConverter.cs @@ -132,6 +132,19 @@ public static OutputKind ToOutputKind(string propertyValue) } } + public static NullableContextOptions ToNullableContextOptions(string propertyValue) + { + switch (propertyValue?.ToLowerInvariant()) + { + case "disable": return NullableContextOptions.Disable; + case "enable": return NullableContextOptions.Enable; + case "safeonly": return NullableContextOptions.SafeOnly; + case "warnings": return NullableContextOptions.Warnings; + case "safeonlywarnings": return NullableContextOptions.SafeOnlyWarnings; + default: return NullableContextOptions.Disable; + } + } + public static VersionRange ToVersionRange(string propertyValue) { if (VersionRange.TryParse(propertyValue.Trim(), out var version)) diff --git a/src/OmniSharp.MSBuild/ProjectFile/PropertyNames.cs b/src/OmniSharp.MSBuild/ProjectFile/PropertyNames.cs index 8aaab92371..26063e70ec 100644 --- a/src/OmniSharp.MSBuild/ProjectFile/PropertyNames.cs +++ b/src/OmniSharp.MSBuild/ProjectFile/PropertyNames.cs @@ -21,6 +21,7 @@ internal static class PropertyNames public const string MSBuildExtensionsPath = nameof(MSBuildExtensionsPath); public const string MSBuildSDKsPath = nameof(MSBuildSDKsPath); public const string NoWarn = nameof(NoWarn); + public const string NullableContextOptions = nameof(NullableContextOptions); public const string OutputPath = nameof(OutputPath); public const string Platform = nameof(Platform); public const string ProjectAssetsFile = nameof(ProjectAssetsFile); diff --git a/src/OmniSharp.MSBuild/ProjectIdInfo.cs b/src/OmniSharp.MSBuild/ProjectIdInfo.cs new file mode 100644 index 0000000000..b1a46a1b26 --- /dev/null +++ b/src/OmniSharp.MSBuild/ProjectIdInfo.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.CodeAnalysis; + +namespace OmniSharp.MSBuild +{ + public class ProjectIdInfo + { + public ProjectIdInfo(ProjectId id, bool isDefinedInSolution) + { + Id = id ?? throw new ArgumentNullException(nameof(id)); + IsDefinedInSolution = isDefinedInSolution; + } + + public ProjectId Id { get; set; } + public bool IsDefinedInSolution { get; set; } + } +} diff --git a/src/OmniSharp.MSBuild/ProjectLoadListener.cs b/src/OmniSharp.MSBuild/ProjectLoadListener.cs new file mode 100644 index 0000000000..c4aa6fe415 --- /dev/null +++ b/src/OmniSharp.MSBuild/ProjectLoadListener.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Composition; +using System.IO; +using System.Linq; +using Microsoft.Build.Execution; +using Microsoft.Extensions.Logging; +using OmniSharp.Eventing; +using OmniSharp.Models; +using OmniSharp.MSBuild.Notification; + +namespace OmniSharp.MSBuild +{ + [Export(typeof(IMSBuildEventSink))] + public class ProjectLoadListener : IMSBuildEventSink + { + internal const string TargetFramework = nameof(TargetFramework); + internal const string TargetFrameworkVersion = nameof(TargetFrameworkVersion); + internal const string TargetFrameworks = nameof(TargetFrameworks); + private const string MSBuildProjectFullPath = nameof(MSBuildProjectFullPath); + private readonly ILogger _logger; + private readonly IEventEmitter _eventEmitter; + private static readonly VsTfmAndFileExtHashingAlgorithm _tfmAndFileHashingAlgorithm = new VsTfmAndFileExtHashingAlgorithm(); + private static readonly VsReferenceHashingAlgorithm _referenceHashingAlgorithm = new VsReferenceHashingAlgorithm(); + + [ImportingConstructor] + public ProjectLoadListener(ILoggerFactory loggerFactory, IEventEmitter eventEmitter) + { + _logger = loggerFactory.CreateLogger(); + _eventEmitter = eventEmitter; + } + + public void ProjectLoaded(ProjectLoadedEventArgs args) + { + try + { + var projectGuid = GetProjectId(args); + var hashedTargetFrameworks = GetHashedTargetFrameworks(args.ProjectInstance); + + if (args.References == null) + { + return; + } + + var hashedReferences = GetHashedReferences(args); + var hashedFileExtensions = GetUniqueHashedFileExtensions(args); + + _eventEmitter.ProjectInformation(projectGuid, hashedTargetFrameworks, hashedReferences, hashedFileExtensions); + } + catch (Exception ex) + { + _logger.LogError("Unexpected exception got thrown from project load listener: " + ex); + } + } + + private static IEnumerable GetUniqueHashedFileExtensions(ProjectLoadedEventArgs args) + { + IEnumerable sourceFileExtensions = args.SourceFiles.Select(file => Path.GetExtension(file)).Distinct(); + return sourceFileExtensions.Select(_tfmAndFileHashingAlgorithm.HashInput); + } + + private static HashedString GetProjectId(ProjectLoadedEventArgs args) + { + if (args.ProjectIdIsDefinedInSolution) + { + //If we are getting a raw guid we should not hash it + return new HashedString(args.Id.Id.ToString()); + } + + var projectFilePath = args.ProjectInstance.GetPropertyValue(MSBuildProjectFullPath); + var content = File.ReadAllText(projectFilePath); + //create a hash from the filename and the content + return _referenceHashingAlgorithm.HashInput($"Filename: {Path.GetFileName(projectFilePath)}\n{content}"); + } + + private static IEnumerable GetHashedReferences(ProjectLoadedEventArgs args) + { + var referenceNames = args.References.Select(reference => Path.GetFileNameWithoutExtension(reference).ToLower()); + return referenceNames.Select(_referenceHashingAlgorithm.HashInput); + } + + // Internal for testing + internal static IEnumerable GetHashedTargetFrameworks(ProjectInstance projectInstance) + { + var targetFrameworks = projectInstance.GetPropertyValue(TargetFrameworks); + if (string.IsNullOrEmpty(targetFrameworks)) + { + targetFrameworks = projectInstance.GetPropertyValue(TargetFramework); + } + if (string.IsNullOrEmpty(targetFrameworks)) + { + targetFrameworks = projectInstance.GetPropertyValue(TargetFrameworkVersion); + } + + if (!string.IsNullOrEmpty(targetFrameworks)) + { + return targetFrameworks.Split(';') + .Where(tfm => !string.IsNullOrWhiteSpace(tfm)) + .Select(tfm => tfm.ToLower()) + .Select(_tfmAndFileHashingAlgorithm.HashInput); + } + + return new List(); + } + } +} diff --git a/src/OmniSharp.MSBuild/ProjectManager.cs b/src/OmniSharp.MSBuild/ProjectManager.cs index 3b37808548..9d74565a2c 100644 --- a/src/OmniSharp.MSBuild/ProjectManager.cs +++ b/src/OmniSharp.MSBuild/ProjectManager.cs @@ -29,12 +29,14 @@ internal class ProjectManager : DisposableObject { private class ProjectToUpdate { + public ProjectIdInfo ProjectIdInfo; public string FilePath { get; } public bool AllowAutoRestore { get; set; } public ProjectLoadedEventArgs LoadedEventArgs { get; set; } - public ProjectToUpdate(string filePath, bool allowAutoRestore) + public ProjectToUpdate(string filePath, bool allowAutoRestore, ProjectIdInfo projectIdInfo) { + ProjectIdInfo = projectIdInfo ?? throw new ArgumentNullException(nameof(projectIdInfo)); FilePath = filePath ?? throw new ArgumentNullException(nameof(filePath)); AllowAutoRestore = allowAutoRestore; } @@ -61,13 +63,13 @@ public ProjectToUpdate(string filePath, bool allowAutoRestore) private readonly FileSystemNotificationCallback _onDirectoryFileChanged; - public ProjectManager(ILoggerFactory loggerFactory, - MSBuildOptions options, - IEventEmitter eventEmitter, - IFileSystemWatcher fileSystemWatcher, - MetadataFileReferenceCache metadataFileReferenceCache, - PackageDependencyChecker packageDependencyChecker, - ProjectLoader projectLoader, + public ProjectManager(ILoggerFactory loggerFactory, + MSBuildOptions options, + IEventEmitter eventEmitter, + IFileSystemWatcher fileSystemWatcher, + MetadataFileReferenceCache metadataFileReferenceCache, + PackageDependencyChecker packageDependencyChecker, + ProjectLoader projectLoader, OmniSharpWorkspace workspace, ImmutableArray eventSinks) { @@ -114,7 +116,8 @@ private async Task WaitForProjectModelReadyAsync(string documentPath) { if (_projectsRequestedOnDemand.TryAdd(csProjFile, 0 /*unused*/)) { - QueueProjectUpdate(csProjFile, allowAutoRestore:true); + var projectIdInfo = new ProjectIdInfo(ProjectId.CreateNewId(csProjFile), false); + QueueProjectUpdate(csProjFile, allowAutoRestore:true, projectIdInfo); } } @@ -123,7 +126,7 @@ private async Task WaitForProjectModelReadyAsync(string documentPath) projectDir = Path.GetDirectoryName(projectDir); } while(projectDir != null); - + // Wait for all queued projects to load to ensure that workspace is fully up to date before this method completes. // If the project for the document was loaded before and there are no other projects to load at the moment, the call below will be no-op. _logger.LogTrace($"Started waiting for projects queue to be empty when requested '{documentPath}'"); @@ -145,10 +148,10 @@ protected override void DisposeCore(bool disposing) public IEnumerable GetAllProjects() => _projectFiles.GetItems(); public bool TryGetProject(string projectFilePath, out ProjectFileInfo projectFileInfo) => _projectFiles.TryGetValue(projectFilePath, out projectFileInfo); - public void QueueProjectUpdate(string projectFilePath, bool allowAutoRestore) + public void QueueProjectUpdate(string projectFilePath, bool allowAutoRestore, ProjectIdInfo projectId) { _logger.LogInformation($"Queue project update for '{projectFilePath}'"); - _queue.Post(new ProjectToUpdate(projectFilePath, allowAutoRestore)); + _queue.Post(new ProjectToUpdate(projectFilePath, allowAutoRestore, projectId)); } public async Task WaitForQueueEmptyAsync() @@ -227,7 +230,7 @@ private void ProcessQueue(CancellationToken cancellationToken) } else { - (projectFileInfo, loadedEventArgs) = LoadProject(currentProject.FilePath); + (projectFileInfo, loadedEventArgs) = LoadProject(currentProject.FilePath, currentProject.ProjectIdInfo); if (projectFileInfo == null) { _failedToLoadProjectFiles.Add(currentProject.FilePath); @@ -279,8 +282,8 @@ private void ProcessQueue(CancellationToken cancellationToken) _fileSystemWatcher.Watch(".cs", _onDirectoryFileChanged); } - private (ProjectFileInfo, ProjectLoadedEventArgs) LoadProject(string projectFilePath) - => LoadOrReloadProject(projectFilePath, () => ProjectFileInfo.Load(projectFilePath, _projectLoader)); + private (ProjectFileInfo, ProjectLoadedEventArgs) LoadProject(string projectFilePath, ProjectIdInfo idInfo) + => LoadOrReloadProject(projectFilePath, () => ProjectFileInfo.Load(projectFilePath, idInfo, _projectLoader)); private (ProjectFileInfo, ProjectLoadedEventArgs) ReloadProject(ProjectFileInfo projectFileInfo) => LoadOrReloadProject(projectFileInfo.FilePath, () => projectFileInfo.Reload(_projectLoader)); @@ -361,14 +364,14 @@ private void WatchProjectFiles(ProjectFileInfo projectFileInfo) // as "updates". We should properly remove projects that are deleted. _fileSystemWatcher.Watch(projectFileInfo.FilePath, (file, changeType) => { - QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: true); + QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: true, projectFileInfo.ProjectIdInfo); }); if (!string.IsNullOrEmpty(projectFileInfo.ProjectAssetsFile)) { _fileSystemWatcher.Watch(projectFileInfo.ProjectAssetsFile, (file, changeType) => { - QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false); + QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false, projectFileInfo.ProjectIdInfo); }); var restoreDirectory = Path.GetDirectoryName(projectFileInfo.ProjectAssetsFile); @@ -379,17 +382,17 @@ private void WatchProjectFiles(ProjectFileInfo projectFileInfo) _fileSystemWatcher.Watch(nugetCacheFile, (file, changeType) => { - QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false); + QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false, projectFileInfo.ProjectIdInfo); }); _fileSystemWatcher.Watch(nugetPropsFile, (file, changeType) => { - QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false); + QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false, projectFileInfo.ProjectIdInfo); }); _fileSystemWatcher.Watch(nugetTargetsFile, (file, changeType) => { - QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false); + QueueProjectUpdate(projectFileInfo.FilePath, allowAutoRestore: false, projectFileInfo.ProjectIdInfo); }); } } @@ -418,7 +421,11 @@ private void UpdateProject(string projectFilePath) private void UpdateSourceFiles(Project project, IList sourceFiles) { - var currentDocuments = project.Documents.ToDictionary(d => d.FilePath, d => d.Id); + // Remove transient documents from list of current documents, to assure proper new documents are added. + // Transient documents will be removed on workspace DocumentAdded event. + var currentDocuments = project.Documents + .Where(document => !_workspace.BufferManager.IsTransientDocument(document.Id)) + .ToDictionary(d => d.FilePath, d => d.Id); // Add source files to the project. foreach (var sourceFile in sourceFiles) @@ -522,7 +529,7 @@ private void UpdateProjectReferences(Project project, ImmutableArray pro referencedProject = ProjectFileInfo.CreateNoBuild(projectReferencePath, _projectLoader); AddProject(referencedProject); - QueueProjectUpdate(projectReferencePath, allowAutoRestore: true); + QueueProjectUpdate(projectReferencePath, allowAutoRestore: true, referencedProject.ProjectIdInfo); } } @@ -585,6 +592,21 @@ private void UpdateReferences(Project project, ImmutableArray projectRef if (!referencesToAdd.Contains(reference)) { + if (_projectFiles.TryGetValue(project.FilePath, out var projectFileInfo)) + { + if (projectFileInfo.ReferenceAliases != null && projectFileInfo.ReferenceAliases.TryGetValue(referencePath, out var aliases)) + { + if (!string.IsNullOrEmpty(aliases)) + { + reference = reference.WithAliases(aliases.Split(';')); + _logger.LogDebug($"setting aliases: {referencePath}, {aliases} "); + } + } + } + else + { + _logger.LogDebug($"failed to get project info:{project.FilePath}"); + } _logger.LogDebug($"Adding reference '{referencePath}' to '{project.Name}'."); _workspace.AddMetadataReference(project.Id, reference); referencesToAdd.Add(reference); diff --git a/src/OmniSharp.MSBuild/ProjectSystem.cs b/src/OmniSharp.MSBuild/ProjectSystem.cs index 0165e45067..a5fede9d27 100644 --- a/src/OmniSharp.MSBuild/ProjectSystem.cs +++ b/src/OmniSharp.MSBuild/ProjectSystem.cs @@ -99,7 +99,7 @@ public void Initalize(IConfiguration configuration) _packageDependencyChecker = new PackageDependencyChecker(_loggerFactory, _eventEmitter, _dotNetCli, _options); _loader = new ProjectLoader(_options, _environment.TargetDirectory, _propertyOverrides, _loggerFactory, _sdksPathResolver); - _manager = new ProjectManager(_loggerFactory, _options, _eventEmitter, _fileSystemWatcher, _metadataFileReferenceCache, _packageDependencyChecker, + _manager = new ProjectManager(_loggerFactory, _options, _eventEmitter, _fileSystemWatcher, _metadataFileReferenceCache, _packageDependencyChecker, _loader, _workspace, _eventSinks); if (_options.LoadProjectsOnDemand) @@ -108,9 +108,9 @@ public void Initalize(IConfiguration configuration) return; } - var initialProjectPaths = GetInitialProjectPaths(); + var initialProjectPathsAndIds = GetInitialProjectPathsAndIds(); - foreach (var projectFilePath in initialProjectPaths) + foreach (var (projectFilePath, projectIdInfo) in initialProjectPathsAndIds) { if (!File.Exists(projectFilePath)) { @@ -118,17 +118,17 @@ public void Initalize(IConfiguration configuration) continue; } - _manager.QueueProjectUpdate(projectFilePath, allowAutoRestore: true); + _manager.QueueProjectUpdate(projectFilePath, allowAutoRestore: true, projectIdInfo); } } - private IEnumerable GetInitialProjectPaths() + private IEnumerable<(string, ProjectIdInfo)> GetInitialProjectPathsAndIds() { // If a solution was provided, use it. if (!string.IsNullOrEmpty(_environment.SolutionFilePath)) { _solutionFileOrRootPath = _environment.SolutionFilePath; - return GetProjectPathsFromSolution(_environment.SolutionFilePath); + return GetProjectPathsAndIdsFromSolution(_environment.SolutionFilePath); } // Otherwise, assume that the path provided is a directory and look for a solution there. @@ -136,22 +136,27 @@ private IEnumerable GetInitialProjectPaths() if (!string.IsNullOrEmpty(solutionFilePath)) { _solutionFileOrRootPath = solutionFilePath; - return GetProjectPathsFromSolution(solutionFilePath); + return GetProjectPathsAndIdsFromSolution(solutionFilePath); } // Finally, if there isn't a single solution immediately available, // Just process all of the projects beneath the root path. _solutionFileOrRootPath = _environment.TargetDirectory; - return _fileSystemHelper.GetFiles("**/*.csproj"); + return _fileSystemHelper.GetFiles("**/*.csproj") + .Select(filepath => + { + var projectIdInfo = new ProjectIdInfo(ProjectId.CreateNewId(debugName: filepath), isDefinedInSolution: false); + return (filepath, projectIdInfo); + }); } - private IEnumerable GetProjectPathsFromSolution(string solutionFilePath) + private IEnumerable<(string, ProjectIdInfo)> GetProjectPathsAndIdsFromSolution(string solutionFilePath) { _logger.LogInformation($"Detecting projects in '{solutionFilePath}'."); var solutionFile = SolutionFile.ParseFile(solutionFilePath); var processedProjects = new HashSet(StringComparer.OrdinalIgnoreCase); - var result = new List(); + var result = new List<(string, ProjectIdInfo)>(); foreach (var project in solutionFile.Projects) { @@ -173,7 +178,8 @@ private IEnumerable GetProjectPathsFromSolution(string solutionFilePath) if (string.Equals(Path.GetExtension(projectFilePath), ".csproj", StringComparison.OrdinalIgnoreCase)) { - result.Add(projectFilePath); + var projectIdInfo = new ProjectIdInfo(ProjectId.CreateFromSerialized(new Guid(project.ProjectGuid)), true); + result.Add((projectFilePath, projectIdInfo)); } processedProjects.Add(projectFilePath); diff --git a/src/OmniSharp.MSBuild/VsReferenceHashingAlgorithm.cs b/src/OmniSharp.MSBuild/VsReferenceHashingAlgorithm.cs new file mode 100644 index 0000000000..a02d2300b8 --- /dev/null +++ b/src/OmniSharp.MSBuild/VsReferenceHashingAlgorithm.cs @@ -0,0 +1,121 @@ +using OmniSharp.Models; +using System.Text; + +namespace OmniSharp.MSBuild +{ + public class VsReferenceHashingAlgorithm : IHasher + { + private static readonly ulong[] _hashText = { + 0x0000000000000000, 0x0809e8a2969451e9, 0x1013d1452d28a3d2, 0x181a39e7bbbcf23b, + 0x2027a28a5a5147a4, 0x282e4a28ccc5164d, 0x303473cf7779e476, 0x383d9b6de1edb59f, + 0x404f4514b4a28f48, 0x4846adb62236dea1, 0x505c9451998a2c9a, 0x58557cf30f1e7d73, + 0x6068e79eeef3c8ec, 0x68610f3c78679905, 0x707b36dbc3db6b3e, 0x7872de79554f3ad7, + 0x809e8a2969451e90, 0x8897628bffd14f79, 0x908d5b6c446dbd42, 0x9884b3ced2f9ecab, + 0xa0b928a333145934, 0xa8b0c001a58008dd, 0xb0aaf9e61e3cfae6, 0xb8a3114488a8ab0f, + 0xc0d1cf3ddde791d8, 0xc8d8279f4b73c031, 0xd0c21e78f0cf320a, 0xd8cbf6da665b63e3, + 0xe0f66db787b6d67c, 0xe8ff851511228795, 0xf0e5bcf2aa9e75ae, 0xf8ec54503c0a2447, + 0x24b1909974c84e69, 0x2cb8783be25c1f80, 0x34a241dc59e0edbb, 0x3caba97ecf74bc52, + 0x049632132e9909cd, 0x0c9fdab1b80d5824, 0x1485e35603b1aa1f, 0x1c8c0bf49525fbf6, + 0x64fed58dc06ac121, 0x6cf73d2f56fe90c8, 0x74ed04c8ed4262f3, 0x7ce4ec6a7bd6331a, + 0x44d977079a3b8685, 0x4cd09fa50cafd76c, 0x54caa642b7132557, 0x5cc34ee0218774be, + 0xa42f1ab01d8d50f9, 0xac26f2128b190110, 0xb43ccbf530a5f32b, 0xbc352357a631a2c2, + 0x8408b83a47dc175d, 0x8c015098d14846b4, 0x941b697f6af4b48f, 0x9c1281ddfc60e566, + 0xe4605fa4a92fdfb1, 0xec69b7063fbb8e58, 0xf4738ee184077c63, 0xfc7a664312932d8a, + 0xc447fd2ef37e9815, 0xcc4e158c65eac9fc, 0xd4542c6bde563bc7, 0xdc5dc4c948c26a2e, + 0x49632132e9909cd2, 0x416ac9907f04cd3b, 0x5970f077c4b83f00, 0x517918d5522c6ee9, + 0x694483b8b3c1db76, 0x614d6b1a25558a9f, 0x795752fd9ee978a4, 0x715eba5f087d294d, + 0x092c64265d32139a, 0x01258c84cba64273, 0x193fb563701ab048, 0x11365dc1e68ee1a1, + 0x290bc6ac0763543e, 0x21022e0e91f705d7, 0x391817e92a4bf7ec, 0x3111ff4bbcdfa605, + 0xc9fdab1b80d58242, 0xc1f443b91641d3ab, 0xd9ee7a5eadfd2190, 0xd1e792fc3b697079, + 0xe9da0991da84c5e6, 0xe1d3e1334c10940f, 0xf9c9d8d4f7ac6634, 0xf1c03076613837dd, + 0x89b2ee0f34770d0a, 0x81bb06ada2e35ce3, 0x99a13f4a195faed8, 0x91a8d7e88fcbff31, + 0xa9954c856e264aae, 0xa19ca427f8b21b47, 0xb9869dc0430ee97c, 0xb18f7562d59ab895, + 0x6dd2b1ab9d58d2bb, 0x65db59090bcc8352, 0x7dc160eeb0707169, 0x75c8884c26e42080, + 0x4df51321c709951f, 0x45fcfb83519dc4f6, 0x5de6c264ea2136cd, 0x55ef2ac67cb56724, + 0x2d9df4bf29fa5df3, 0x25941c1dbf6e0c1a, 0x3d8e25fa04d2fe21, 0x3587cd589246afc8, + 0x0dba563573ab1a57, 0x05b3be97e53f4bbe, 0x1da987705e83b985, 0x15a06fd2c817e86c, + 0xed4c3b82f41dcc2b, 0xe545d32062899dc2, 0xfd5feac7d9356ff9, 0xf55602654fa13e10, + 0xcd6b9908ae4c8b8f, 0xc56271aa38d8da66, 0xdd78484d8364285d, 0xd571a0ef15f079b4, + 0xad037e9640bf4363, 0xa50a9634d62b128a, 0xbd10afd36d97e0b1, 0xb5194771fb03b158, + 0x8d24dc1c1aee04c7, 0x852d34be8c7a552e, 0x9d370d5937c6a715, 0x953ee5fba152f6fc, + 0x92c64265d32139a4, 0x9acfaac745b5684d, 0x82d59320fe099a76, 0x8adc7b82689dcb9f, + 0xb2e1e0ef89707e00, 0xbae8084d1fe42fe9, 0xa2f231aaa458ddd2, 0xaafbd90832cc8c3b, + 0xd28907716783b6ec, 0xda80efd3f117e705, 0xc29ad6344aab153e, 0xca933e96dc3f44d7, + 0xf2aea5fb3dd2f148, 0xfaa74d59ab46a0a1, 0xe2bd74be10fa529a, 0xeab49c1c866e0373, + 0x1258c84cba642734, 0x1a5120ee2cf076dd, 0x024b1909974c84e6, 0x0a42f1ab01d8d50f, + 0x327f6ac6e0356090, 0x3a76826476a13179, 0x226cbb83cd1dc342, 0x2a6553215b8992ab, + 0x52178d580ec6a87c, 0x5a1e65fa9852f995, 0x42045c1d23ee0bae, 0x4a0db4bfb57a5a47, + 0x72302fd25497efd8, 0x7a39c770c203be31, 0x6223fe9779bf4c0a, 0x6a2a1635ef2b1de3, + 0xb677d2fca7e977cd, 0xbe7e3a5e317d2624, 0xa66403b98ac1d41f, 0xae6deb1b1c5585f6, + 0x96507076fdb83069, 0x9e5998d46b2c6180, 0x8643a133d09093bb, 0x8e4a49914604c252, + 0xf63897e8134bf885, 0xfe317f4a85dfa96c, 0xe62b46ad3e635b57, 0xee22ae0fa8f70abe, + 0xd61f3562491abf21, 0xde16ddc0df8eeec8, 0xc60ce42764321cf3, 0xce050c85f2a64d1a, + 0x36e958d5ceac695d, 0x3ee0b077583838b4, 0x26fa8990e384ca8f, 0x2ef3613275109b66, + 0x16cefa5f94fd2ef9, 0x1ec712fd02697f10, 0x06dd2b1ab9d58d2b, 0x0ed4c3b82f41dcc2, + 0x76a61dc17a0ee615, 0x7eaff563ec9ab7fc, 0x66b5cc84572645c7, 0x6ebc2426c1b2142e, + 0x5681bf4b205fa1b1, 0x5e8857e9b6cbf058, 0x46926e0e0d770263, 0x4e9b86ac9be3538a, + 0xdba563573ab1a576, 0xd3ac8bf5ac25f49f, 0xcbb6b212179906a4, 0xc3bf5ab0810d574d, + 0xfb82c1dd60e0e2d2, 0xf38b297ff674b33b, 0xeb9110984dc84100, 0xe398f83adb5c10e9, + 0x9bea26438e132a3e, 0x93e3cee118877bd7, 0x8bf9f706a33b89ec, 0x83f01fa435afd805, + 0xbbcd84c9d4426d9a, 0xb3c46c6b42d63c73, 0xabde558cf96ace48, 0xa3d7bd2e6ffe9fa1, + 0x5b3be97e53f4bbe6, 0x533201dcc560ea0f, 0x4b28383b7edc1834, 0x4321d099e84849dd, + 0x7b1c4bf409a5fc42, 0x7315a3569f31adab, 0x6b0f9ab1248d5f90, 0x63067213b2190e79, + 0x1b74ac6ae75634ae, 0x137d44c871c26547, 0x0b677d2fca7e977c, 0x036e958d5ceac695, + 0x3b530ee0bd07730a, 0x335ae6422b9322e3, 0x2b40dfa5902fd0d8, 0x2349370706bb8131, + 0xff14f3ce4e79eb1f, 0xf71d1b6cd8edbaf6, 0xef07228b635148cd, 0xe70eca29f5c51924, + 0xdf3351441428acbb, 0xd73ab9e682bcfd52, 0xcf20800139000f69, 0xc72968a3af945e80, + 0xbf5bb6dafadb6457, 0xb7525e786c4f35be, 0xaf48679fd7f3c785, 0xa7418f3d4167966c, + 0x9f7c1450a08a23f3, 0x9775fcf2361e721a, 0x8f6fc5158da28021, 0x87662db71b36d1c8, + 0x7f8a79e7273cf58f, 0x77839145b1a8a466, 0x6f99a8a20a14565d, 0x679040009c8007b4, + 0x5faddb6d7d6db22b, 0x57a433cfebf9e3c2, 0x4fbe0a28504511f9, 0x47b7e28ac6d14010, + 0x3fc53cf3939e7ac7, 0x37ccd451050a2b2e, 0x2fd6edb6beb6d915, 0x27df0514282288fc, + 0x1fe29e79c9cf3d63, 0x17eb76db5f5b6c8a, 0x0ff14f3ce4e79eb1, 0x07f8a79e7273cf58, + }; + + /// + /// The format in which the input stream should be read.abstract Ulong bitmask + /// + private static readonly ulong _mask = 0xffffffffffffffffUL; + + + /// + /// Helper method to encode an input string in Unicode format before feeding through masking into hash function + /// + /// The string to be encoded + /// An array of byte in unicode format (wchars) + private static byte[] EncodeBytes(string cleartext) + { + return Encoding.Unicode.GetBytes(cleartext); + } + + /// + /// Calculates the hashed value + /// + /// Array of byte that repersents the value that needs hashing + /// The format in which the input stream should be read: 0xffffffffffffffffUL + /// A numerical value that is the hash + private static ulong ComputeHash(byte[] inputstream, ulong crc) + { + for (int j = 0; j < inputstream.Length; j++) + { + crc = _hashText[(byte)(crc ^ inputstream[j])] ^ (crc >> 8); + } + + return crc; + + } + + /// + /// Calls the hashing function on the input + /// + /// The value that should be hashed + /// A string representation of the hash value + public HashedString HashInput(string cleartext) + { + ulong hashedBytes = ComputeHash(EncodeBytes(cleartext), _mask); + + return new HashedString(hashedBytes.ToString("x")); + + } + } +} diff --git a/src/OmniSharp.MSBuild/VsTfmAndFileExtHashingAlgorithm.cs b/src/OmniSharp.MSBuild/VsTfmAndFileExtHashingAlgorithm.cs new file mode 100644 index 0000000000..e7b0efcde0 --- /dev/null +++ b/src/OmniSharp.MSBuild/VsTfmAndFileExtHashingAlgorithm.cs @@ -0,0 +1,57 @@ +using OmniSharp.Models; + +namespace OmniSharp.MSBuild +{ + public class VsTfmAndFileExtHashingAlgorithm : IHasher + { + static readonly long[] cr3tab = /* CRC polynomial 0xedb88320 */ + { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL + }; + + + public HashedString HashInput(string cleartext) + { + long lHash = 0; + + for (int i = 0; i < cleartext.Length; i++) + { + lHash = (cr3tab[((int)lHash ^ cleartext[i]) & 0xff] ^ ((lHash >> 8) & 0x00FFFFFFL)); + } + + int result = (int)lHash; // need to cast it to a DWORD + return new HashedString(result.ToString()); + } + } +} diff --git a/src/OmniSharp.Roslyn.CSharp/OmniSharp.Roslyn.CSharp.csproj b/src/OmniSharp.Roslyn.CSharp/OmniSharp.Roslyn.CSharp.csproj index bc165ff264..1f05661ac5 100644 --- a/src/OmniSharp.Roslyn.CSharp/OmniSharp.Roslyn.CSharp.csproj +++ b/src/OmniSharp.Roslyn.CSharp/OmniSharp.Roslyn.CSharp.csproj @@ -8,9 +8,11 @@ + + diff --git a/src/OmniSharp.Roslyn.CSharp/Services/Types/TypeLookup.cs b/src/OmniSharp.Roslyn.CSharp/Services/Types/TypeLookup.cs index 16df110506..89fc1a4bd5 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/Types/TypeLookup.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/Types/TypeLookup.cs @@ -20,6 +20,15 @@ public class TypeLookupService : IRequestHandler Handle(TypeLookupRequest request) { response.Type = symbol.Kind == SymbolKind.NamedType ? symbol.ToDisplayString(DefaultFormat) : - symbol.ToMinimalDisplayString(semanticModel, position); + symbol.ToMinimalDisplayString(semanticModel, position, MinimalFormat); if (request.IncludeDocumentation) { diff --git a/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticService.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticService.cs index 03e733f669..f199b90af8 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Diagnostics/CSharpDiagnosticService.cs @@ -1,7 +1,14 @@ +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Composition; using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reactive.Threading; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; @@ -18,8 +25,7 @@ public class CSharpDiagnosticService private readonly OmniSharpWorkspace _workspace; private readonly object _lock = new object(); private readonly DiagnosticEventForwarder _forwarder; - private bool _queueRunning = false; - private readonly ConcurrentQueue _openDocuments = new ConcurrentQueue(); + private readonly IObserver _openDocuments; [ImportingConstructor] public CSharpDiagnosticService(OmniSharpWorkspace workspace, DiagnosticEventForwarder forwarder, ILoggerFactory loggerFactory) @@ -28,113 +34,92 @@ public CSharpDiagnosticService(OmniSharpWorkspace workspace, DiagnosticEventForw _forwarder = forwarder; _logger = loggerFactory.CreateLogger(); + var openDocumentsSubject = new Subject(); + _openDocuments = openDocumentsSubject; + _workspace.WorkspaceChanged += OnWorkspaceChanged; + _workspace.DocumentOpened += OnDocumentOpened; + _workspace.DocumentClosed += OnDocumentOpened; + + openDocumentsSubject + .GroupByUntil(x => true, group => Observable.Amb( + group.Throttle(TimeSpan.FromMilliseconds(200)), + group.Distinct().Skip(99)) + ) + .Select(x => x.ToArray()) + .Merge() + .SubscribeOn(TaskPoolScheduler.Default) + .Select(ProcessQueue) + .Merge() + .Subscribe(); } - private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs changeEvent) + private void OnDocumentOpened(object sender, DocumentEventArgs args) { - if (changeEvent.Kind == WorkspaceChangeKind.DocumentChanged) + if (!_forwarder.IsEnabled) { - var newDocument = changeEvent.NewSolution.GetDocument(changeEvent.DocumentId); - - this.EmitDiagnostics(newDocument.FilePath); - foreach (var document in _workspace.GetOpenDocumentIds().Select(x => _workspace.CurrentSolution.GetDocument(x))) - { - this.EmitDiagnostics(document.FilePath); - } + return; } - } - public void QueueDiagnostics(params string[] documents) - { - this.EmitDiagnostics(documents); + EmitDiagnostics(args.Document.FilePath); + EmitDiagnostics(_workspace.GetOpenDocumentIds().Select(x => _workspace.CurrentSolution.GetDocument(x).FilePath).ToArray()); } - private void EmitDiagnostics(params string[] documents) + private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs changeEvent) { - if (_forwarder.IsEnabled) + if (!_forwarder.IsEnabled) { - foreach (var document in documents) - { - if (!_openDocuments.Contains(document)) - { - _openDocuments.Enqueue(document); - } - } + return; + } - if (!_queueRunning && !_openDocuments.IsEmpty) - { - this.ProcessQueue(); - } + if (changeEvent.Kind == WorkspaceChangeKind.DocumentAdded || changeEvent.Kind == WorkspaceChangeKind.DocumentChanged || changeEvent.Kind == WorkspaceChangeKind.DocumentReloaded) + { + var newDocument = changeEvent.NewSolution.GetDocument(changeEvent.DocumentId); + + EmitDiagnostics(newDocument.FilePath); + EmitDiagnostics(_workspace.GetOpenDocumentIds().Select(x => _workspace.CurrentSolution.GetDocument(x).FilePath).ToArray()); + } + else if (changeEvent.Kind == WorkspaceChangeKind.ProjectAdded || changeEvent.Kind == WorkspaceChangeKind.ProjectReloaded) + { + EmitDiagnostics(changeEvent.NewSolution.GetProject(changeEvent.ProjectId).Documents.Select(x => x.FilePath).ToArray()); } } - private void ProcessQueue() + public void QueueDiagnostics(params string[] documents) { - lock (_lock) + if (!_forwarder.IsEnabled) { - _queueRunning = true; + return; } - Task.Factory.StartNew(async () => - { - await Task.Delay(100); - await Dequeue(); - - if (_openDocuments.IsEmpty) - { - lock (_lock) - { - _queueRunning = false; - } - } - else - { - this.ProcessQueue(); - } - }); + this.EmitDiagnostics(documents); } - private async Task Dequeue() + private void EmitDiagnostics(params string[] documents) { - var tasks = new List>(); - for (var i = 0; i < 50; i++) + if (!_forwarder.IsEnabled) { - if (_openDocuments.IsEmpty) - { - break; - } - - if (_openDocuments.TryDequeue(out var filePath)) - { - tasks.Add(this.ProcessNextItem(filePath)); - } + return; } - if (!tasks.Any()) return; + foreach (var document in documents) + { + _openDocuments.OnNext(document); + } + } - var diagnosticResults = await Task.WhenAll(tasks.ToArray()); - if (diagnosticResults.Any()) + private IObservable ProcessQueue(IEnumerable filePaths) + { + return Observable.FromAsync(async () => { + var results = await Task.WhenAll(filePaths.Distinct().Select(ProcessNextItem)); var message = new DiagnosticMessage() { - Results = diagnosticResults + Results = results }; - this._forwarder.Forward(message); - } - - if (_openDocuments.IsEmpty) - { - lock (_lock) - { - _queueRunning = false; - } - } - else - { - this.ProcessQueue(); - } + _forwarder.Forward(message); + }); } private async Task ProcessNextItem(string filePath) diff --git a/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs b/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs index d6dc9b9734..d64b17a659 100644 --- a/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs +++ b/src/OmniSharp.Roslyn.CSharp/Workers/Refactoring/FixUsingsWorker.cs @@ -51,80 +51,60 @@ private static CodeFixProvider FindCodeFixProviderByTypeFullName(IEnumerable FixUsingsAsync(Document document) { - document = await AddMissingUsingsAsync(document); + var missingUsings = await AddMissingUsingsAsync(document); + + document = missingUsings.Document; document = await RemoveUnnecessaryUsingsAsync(document); document = await TryAddLinqQuerySyntaxAsync(document); - var ambiguous = await GetAmbiguousUsingsAsync(document); - - var response = new FixUsingsWorkerResponse() + return new FixUsingsWorkerResponse() { - AmbiguousResults = ambiguous, + AmbiguousResults = missingUsings.AmbiguousUsings, Document = document }; - - response.AmbiguousResults = ambiguous; - response.Document = document; - - return response; } - private async Task> GetAmbiguousUsingsAsync(Document document) + private async Task TrackAmbiguousQuickFix(IList results, IList ambiguousNodes, SimpleNameSyntax name, ImmutableArray operations, Document document) { - var ambiguousNodes = new List(); - var results = new List(); + ambiguousNodes.Add(name); + var unresolvedText = name.Identifier.ValueText; + var unresolvedLocation = name.GetLocation().GetLineSpan().StartLinePosition; + var ambiguousNamespaces = await GetAmbiguousNamespacesAsync(operations, document); - var semanticModel = await document.GetSemanticModelAsync(); - var root = await semanticModel.SyntaxTree.GetRootAsync(); - - var simpleNames = root - .DescendantNodes() - .OfType() - .Where(x => semanticModel.GetSymbolInfo(x).Symbol == null) - .ToArray(); - - foreach (var name in simpleNames) - { - if (ambiguousNodes.Contains(name)) + results.Add(new QuickFix { - continue; - } - - var diagnostics = await GetDiagnosticsAtSpanAsync(document, name.Identifier.Span, "CS0246", "CS1061", "CS0103"); - if (diagnostics.Any()) - { - var span = diagnostics.First().Location.SourceSpan; - if (diagnostics.Any(d => d.Location.SourceSpan != span)) - { - continue; - } - - var operations = await GetCodeFixOperationsAsync(_addImportProvider, document, span, diagnostics); + Line = unresolvedLocation.Line, + Column = unresolvedLocation.Character, + FileName = document.FilePath, + Text = $"`{unresolvedText}` is ambiguous. Namespaces:{ambiguousNamespaces}" + }); + } - if (operations.Length > 1) - { - // More than one operation - ambiguous - ambiguousNodes.Add(name); - var unresolvedText = name.Identifier.ValueText; - var unresolvedLocation = name.GetLocation().GetLineSpan().StartLinePosition; + private async Task GetAmbiguousNamespacesAsync(ImmutableArray operations, Document document) + { + var namespaces = new List(); + foreach (var operation in operations.Where(x => x is ApplyChangesOperation)) + { + var newSolution = ((ApplyChangesOperation)operation).ChangedSolution; + var newDocument = newSolution.GetDocument(document.Id); - results.Add( - new QuickFix - { - Line = unresolvedLocation.Line, - Column = unresolvedLocation.Character, - FileName = document.FilePath, - Text = "`" + unresolvedText + "`" + " is ambiguous" - }); - } - } + var changes = await newDocument.GetTextChangesAsync(document); + foreach (var change in changes) + namespaces.Add(change.NewText.Trim()); } - return results; + var ambiguousNamespaces = string.Empty; + foreach (var uniqueNamespace in namespaces.Distinct()) + ambiguousNamespaces += $" {uniqueNamespace}"; + + return ambiguousNamespaces; } - private async Task AddMissingUsingsAsync(Document document) + private async Task AddMissingUsingsAsync(Document document) { + var ambiguousNodes = new List(); + var quickFixes = new List(); + while (true) { var semanticModel = await document.GetSemanticModelAsync(); @@ -140,6 +120,11 @@ private async Task AddMissingUsingsAsync(Document document) foreach (var name in unboundNames) { + if (ambiguousNodes.Contains(name)) + { + continue; + } + var diagnostics = await GetDiagnosticsAtSpanAsync(document, name.Identifier.Span, "CS0246", "CS1061", "CS0103"); if (diagnostics.Any()) { @@ -152,7 +137,9 @@ private async Task AddMissingUsingsAsync(Document document) var operations = await GetCodeFixOperationsAsync(_addImportProvider, document, span, diagnostics); - if (operations.Length == 1 && operations[0] is ApplyChangesOperation) + if (operations.Length > 1) + await TrackAmbiguousQuickFix(quickFixes, ambiguousNodes, name, operations, document); + else if (operations.Length == 1 && operations[0] is ApplyChangesOperation) { // Only one operation - apply it and loop back around var newSolution = ((ApplyChangesOperation)operations[0]).ChangedSolution; @@ -172,7 +159,7 @@ private async Task AddMissingUsingsAsync(Document document) } } - return document; + return new MissingUsingsResult { Document = document, AmbiguousUsings = quickFixes }; } private async Task RemoveUnnecessaryUsingsAsync(Document document) @@ -326,5 +313,11 @@ private static bool IsLinqQuerySyntax(SyntaxNode node) return false; } } + + private class MissingUsingsResult + { + public Document Document { get; set; } + public IEnumerable AmbiguousUsings { get; set; } + } } } diff --git a/src/OmniSharp.Roslyn/BufferManager.cs b/src/OmniSharp.Roslyn/BufferManager.cs index 07a3b5e91e..c40b5deaa1 100644 --- a/src/OmniSharp.Roslyn/BufferManager.cs +++ b/src/OmniSharp.Roslyn/BufferManager.cs @@ -29,6 +29,14 @@ public BufferManager(OmniSharpWorkspace workspace, IFileSystemWatcher fileSystem _onFileChanged = OnFileChanged; } + public bool IsTransientDocument(DocumentId documentId) + { + lock(_lock) + { + return _transientDocumentIds.Contains(documentId); + } + } + public async Task UpdateBufferAsync(Request request) { var buffer = request.Buffer; diff --git a/src/OmniSharp.Roslyn/OmniSharp.Roslyn.csproj b/src/OmniSharp.Roslyn/OmniSharp.Roslyn.csproj index a111489d6e..03b646a616 100644 --- a/src/OmniSharp.Roslyn/OmniSharp.Roslyn.csproj +++ b/src/OmniSharp.Roslyn/OmniSharp.Roslyn.csproj @@ -7,6 +7,7 @@ + diff --git a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs index b6d38d6c16..756039c9eb 100644 --- a/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs +++ b/src/OmniSharp.Roslyn/OmniSharpWorkspace.cs @@ -335,7 +335,7 @@ private void SaveDocumentText(DocumentId id, string fullPath, SourceText newText try { var dir = Path.GetDirectoryName(fullPath); - if (!Directory.Exists(dir)) + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } diff --git a/src/OmniSharp.Shared/AssemblyInfo.cs b/src/OmniSharp.Shared/AssemblyInfo.cs new file mode 100644 index 0000000000..6abd59bc91 --- /dev/null +++ b/src/OmniSharp.Shared/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("OmniSharp")] +[assembly: InternalsVisibleTo("OmniSharp.Host")] +[assembly: InternalsVisibleTo("OmniSharp.MSBuild")] +[assembly: InternalsVisibleTo("OmniSharp.Roslyn")] +[assembly: InternalsVisibleTo("OmniSharp.Roslyn.CSharp")] +[assembly: InternalsVisibleTo("OmniSharp.DotNetTest.Tests")] +[assembly: InternalsVisibleTo("OmniSharp.Tests")] diff --git a/src/OmniSharp.Abstractions/FileSystem/FileSystemHelper.cs b/src/OmniSharp.Shared/FileSystem/FileSystemHelper.cs similarity index 100% rename from src/OmniSharp.Abstractions/FileSystem/FileSystemHelper.cs rename to src/OmniSharp.Shared/FileSystem/FileSystemHelper.cs diff --git a/src/OmniSharp.Abstractions/Logging/BaseLogger.cs b/src/OmniSharp.Shared/Logging/BaseLogger.cs similarity index 100% rename from src/OmniSharp.Abstractions/Logging/BaseLogger.cs rename to src/OmniSharp.Shared/Logging/BaseLogger.cs diff --git a/src/OmniSharp.Shared/OmniSharp.Shared.csproj b/src/OmniSharp.Shared/OmniSharp.Shared.csproj new file mode 100644 index 0000000000..0756f4b88c --- /dev/null +++ b/src/OmniSharp.Shared/OmniSharp.Shared.csproj @@ -0,0 +1,22 @@ + + + + net461 + AnyCPU + OmniSharp + + + + + + + + + + + + + + + + diff --git a/src/OmniSharp.Abstractions/Options/FileOptions.cs b/src/OmniSharp.Shared/Options/FileOptions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Options/FileOptions.cs rename to src/OmniSharp.Shared/Options/FileOptions.cs diff --git a/src/OmniSharp.Abstractions/Options/FormattingOptions.cs b/src/OmniSharp.Shared/Options/FormattingOptions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Options/FormattingOptions.cs rename to src/OmniSharp.Shared/Options/FormattingOptions.cs diff --git a/src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs b/src/OmniSharp.Shared/Options/OmniSharpOptions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Options/OmniSharpOptions.cs rename to src/OmniSharp.Shared/Options/OmniSharpOptions.cs diff --git a/src/OmniSharp.Abstractions/Options/RoslynExtensionsOptions.cs b/src/OmniSharp.Shared/Options/RoslynExtensionsOptions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Options/RoslynExtensionsOptions.cs rename to src/OmniSharp.Shared/Options/RoslynExtensionsOptions.cs diff --git a/src/OmniSharp.Abstractions/ProjectSystemNames.cs b/src/OmniSharp.Shared/ProjectSystemNames.cs similarity index 100% rename from src/OmniSharp.Abstractions/ProjectSystemNames.cs rename to src/OmniSharp.Shared/ProjectSystemNames.cs diff --git a/src/OmniSharp.Abstractions/TransportType.cs b/src/OmniSharp.Shared/TransportType.cs similarity index 100% rename from src/OmniSharp.Abstractions/TransportType.cs rename to src/OmniSharp.Shared/TransportType.cs diff --git a/src/OmniSharp.Abstractions/Utilities/CachedStringBuilder.cs b/src/OmniSharp.Shared/Utilities/CachedStringBuilder.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/CachedStringBuilder.cs rename to src/OmniSharp.Shared/Utilities/CachedStringBuilder.cs diff --git a/src/OmniSharp.Abstractions/Utilities/DictionaryExtensions.cs b/src/OmniSharp.Shared/Utilities/DictionaryExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/DictionaryExtensions.cs rename to src/OmniSharp.Shared/Utilities/DictionaryExtensions.cs diff --git a/src/OmniSharp.Abstractions/Utilities/DisposableObject.cs b/src/OmniSharp.Shared/Utilities/DisposableObject.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/DisposableObject.cs rename to src/OmniSharp.Shared/Utilities/DisposableObject.cs diff --git a/src/OmniSharp.Abstractions/Utilities/ImmutableArrayExtensions.cs b/src/OmniSharp.Shared/Utilities/ImmutableArrayExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/ImmutableArrayExtensions.cs rename to src/OmniSharp.Shared/Utilities/ImmutableArrayExtensions.cs diff --git a/src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.DisposableChangeCallback.cs b/src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.DisposableChangeCallback.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.DisposableChangeCallback.cs rename to src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.DisposableChangeCallback.cs diff --git a/src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.PhysicalFileProviderWrapper.cs b/src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.PhysicalFileProviderWrapper.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.PhysicalFileProviderWrapper.cs rename to src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.PhysicalFileProviderWrapper.cs diff --git a/src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.PollingFileChangeToken.cs b/src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.PollingFileChangeToken.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.PollingFileChangeToken.cs rename to src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.PollingFileChangeToken.cs diff --git a/src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.cs b/src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/PhysicalFileProviderExtensions.cs rename to src/OmniSharp.Shared/Utilities/PhysicalFileProviderExtensions.cs diff --git a/src/OmniSharp.Abstractions/Utilities/Platform.cs b/src/OmniSharp.Shared/Utilities/Platform.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/Platform.cs rename to src/OmniSharp.Shared/Utilities/Platform.cs diff --git a/src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs b/src/OmniSharp.Shared/Utilities/PlatformHelper.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/PlatformHelper.cs rename to src/OmniSharp.Shared/Utilities/PlatformHelper.cs diff --git a/src/OmniSharp.Abstractions/Utilities/ProcessExitStatus.cs b/src/OmniSharp.Shared/Utilities/ProcessExitStatus.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/ProcessExitStatus.cs rename to src/OmniSharp.Shared/Utilities/ProcessExitStatus.cs diff --git a/src/OmniSharp.Abstractions/Utilities/ProcessExtensions.cs b/src/OmniSharp.Shared/Utilities/ProcessExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/ProcessExtensions.cs rename to src/OmniSharp.Shared/Utilities/ProcessExtensions.cs diff --git a/src/OmniSharp.Abstractions/Utilities/ProcessHelper.cs b/src/OmniSharp.Shared/Utilities/ProcessHelper.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/ProcessHelper.cs rename to src/OmniSharp.Shared/Utilities/ProcessHelper.cs diff --git a/src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs b/src/OmniSharp.Shared/Utilities/ReflectionExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/ReflectionExtensions.cs rename to src/OmniSharp.Shared/Utilities/ReflectionExtensions.cs diff --git a/src/OmniSharp.Abstractions/Utilities/TaskExtensions.cs b/src/OmniSharp.Shared/Utilities/TaskExtensions.cs similarity index 100% rename from src/OmniSharp.Abstractions/Utilities/TaskExtensions.cs rename to src/OmniSharp.Shared/Utilities/TaskExtensions.cs diff --git a/src/OmniSharp.Stdio.Driver/app.config b/src/OmniSharp.Stdio.Driver/app.config index 4e0d1de440..a725124756 100644 --- a/src/OmniSharp.Stdio.Driver/app.config +++ b/src/OmniSharp.Stdio.Driver/app.config @@ -7,15 +7,15 @@ - + - + - + diff --git a/src/OmniSharp.Stdio/Eventing/StdioEventEmitter.cs b/src/OmniSharp.Stdio/Eventing/StdioEventEmitter.cs index 9564bda337..c9c485a0f0 100644 --- a/src/OmniSharp.Stdio/Eventing/StdioEventEmitter.cs +++ b/src/OmniSharp.Stdio/Eventing/StdioEventEmitter.cs @@ -1,6 +1,6 @@ using OmniSharp.Eventing; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; +using OmniSharp.Protocol; +using OmniSharp.Services; namespace OmniSharp.Stdio.Eventing { diff --git a/src/OmniSharp.Stdio/Host.cs b/src/OmniSharp.Stdio/Host.cs index 6962584cd6..1a034eebb4 100644 --- a/src/OmniSharp.Stdio/Host.cs +++ b/src/OmniSharp.Stdio/Host.cs @@ -14,8 +14,7 @@ using OmniSharp.Models.UpdateBuffer; using OmniSharp.Plugins; using OmniSharp.Services; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; +using OmniSharp.Protocol; using OmniSharp.Utilities; namespace OmniSharp.Stdio diff --git a/src/OmniSharp.Stdio/Logging/StdioLogger.cs b/src/OmniSharp.Stdio/Logging/StdioLogger.cs index eec6667e65..2cefc42743 100644 --- a/src/OmniSharp.Stdio/Logging/StdioLogger.cs +++ b/src/OmniSharp.Stdio/Logging/StdioLogger.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Logging; using OmniSharp.Logging; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; +using OmniSharp.Protocol; +using OmniSharp.Services; namespace OmniSharp.Stdio.Logging { diff --git a/src/OmniSharp.Stdio/Logging/StdioLoggerExtensions.cs b/src/OmniSharp.Stdio/Logging/StdioLoggerExtensions.cs index 3226491425..1a1bb80537 100644 --- a/src/OmniSharp.Stdio/Logging/StdioLoggerExtensions.cs +++ b/src/OmniSharp.Stdio/Logging/StdioLoggerExtensions.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.Logging; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; namespace OmniSharp.Stdio.Logging { diff --git a/src/OmniSharp.Stdio/Logging/StdioLoggerProvider.cs b/src/OmniSharp.Stdio/Logging/StdioLoggerProvider.cs index 04565256ad..b87b55780f 100644 --- a/src/OmniSharp.Stdio/Logging/StdioLoggerProvider.cs +++ b/src/OmniSharp.Stdio/Logging/StdioLoggerProvider.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.Logging; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; namespace OmniSharp.Stdio.Logging { diff --git a/test-assets/test-projects/CSharp8AndNullableContext/CSharp8AndNullableContext.csproj b/test-assets/test-projects/CSharp8AndNullableContext/CSharp8AndNullableContext.csproj new file mode 100644 index 0000000000..f8c35733c7 --- /dev/null +++ b/test-assets/test-projects/CSharp8AndNullableContext/CSharp8AndNullableContext.csproj @@ -0,0 +1,10 @@ + + + + Exe + netcoreapp2.1 + 8.0 + enable + + + \ No newline at end of file diff --git a/test-assets/test-projects/CSharp8AndNullableContext/Program.cs b/test-assets/test-projects/CSharp8AndNullableContext/Program.cs new file mode 100644 index 0000000000..cd8f0e5063 --- /dev/null +++ b/test-assets/test-projects/CSharp8AndNullableContext/Program.cs @@ -0,0 +1,14 @@ +using System; + +namespace ConsoleApplication +{ + public class Program + { + public static void Main(string[] args) + { + string? foo = null; + string bar = null; + Console.WriteLine(foo + bar); + } + } +} \ No newline at end of file diff --git a/test-assets/test-projects/ExternAlias/ExternAlias.App/ExternAlias.App.csproj b/test-assets/test-projects/ExternAlias/ExternAlias.App/ExternAlias.App.csproj new file mode 100644 index 0000000000..f2bf085d0a --- /dev/null +++ b/test-assets/test-projects/ExternAlias/ExternAlias.App/ExternAlias.App.csproj @@ -0,0 +1,18 @@ + + + + Exe + netcoreapp2.1 + + + + + false + + + $(ProjectDir)../ExternAlias.Lib/bin/$(Configuration)/netstandard2.0/ExternAlias.Lib.dll + abc + + + + diff --git a/test-assets/test-projects/ExternAlias/ExternAlias.App/Program.cs b/test-assets/test-projects/ExternAlias/ExternAlias.App/Program.cs new file mode 100644 index 0000000000..3364a344bd --- /dev/null +++ b/test-assets/test-projects/ExternAlias/ExternAlias.App/Program.cs @@ -0,0 +1,14 @@ +extern alias abc; +using System; + +namespace ExternAlias.App +{ + class Program + { + static void Main(string[] args) + { + new abc::ExternAlias.Lib.Class1(); + Console.WriteLine("Hello World!"); + } + } +} diff --git a/test-assets/test-projects/ExternAlias/ExternAlias.Lib/Class1.cs b/test-assets/test-projects/ExternAlias/ExternAlias.Lib/Class1.cs new file mode 100644 index 0000000000..7b90a3ec53 --- /dev/null +++ b/test-assets/test-projects/ExternAlias/ExternAlias.Lib/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace ExternAlias.Lib +{ + public class Class1 + { + } +} diff --git a/test-assets/test-projects/ExternAlias/ExternAlias.Lib/ExternAlias.Lib.csproj b/test-assets/test-projects/ExternAlias/ExternAlias.Lib/ExternAlias.Lib.csproj new file mode 100644 index 0000000000..9f5c4f4abb --- /dev/null +++ b/test-assets/test-projects/ExternAlias/ExternAlias.Lib/ExternAlias.Lib.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/test-assets/test-projects/ExternAlias/ExternAlias.sln b/test-assets/test-projects/ExternAlias/ExternAlias.sln new file mode 100644 index 0000000000..91117aba9a --- /dev/null +++ b/test-assets/test-projects/ExternAlias/ExternAlias.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternAlias.App", "ExternAlias.App\ExternAlias.App.csproj", "{447E6D15-63B0-47F3-9E44-D6F0D0087C46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternAlias.Lib", "ExternAlias.Lib\ExternAlias.Lib.csproj", "{ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|x64.ActiveCfg = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|x64.Build.0 = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|x86.ActiveCfg = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Debug|x86.Build.0 = Debug|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|Any CPU.Build.0 = Release|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|x64.ActiveCfg = Release|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|x64.Build.0 = Release|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|x86.ActiveCfg = Release|Any CPU + {447E6D15-63B0-47F3-9E44-D6F0D0087C46}.Release|x86.Build.0 = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|x64.ActiveCfg = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|x64.Build.0 = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|x86.ActiveCfg = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Debug|x86.Build.0 = Debug|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|Any CPU.Build.0 = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|x64.ActiveCfg = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|x64.Build.0 = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|x86.ActiveCfg = Release|Any CPU + {ECC29DFA-3AC0-474E-9C5C-C8AD4D3DD17D}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/test-assets/test-projects/HelloWorld/HelloWorld.csproj b/test-assets/test-projects/HelloWorld/HelloWorld.csproj index c0318399b6..c9b3d73a3f 100644 --- a/test-assets/test-projects/HelloWorld/HelloWorld.csproj +++ b/test-assets/test-projects/HelloWorld/HelloWorld.csproj @@ -2,20 +2,8 @@ Exe - netcoreapp1.0 - false + netcoreapp2.1 7.1 - - - - - - - - 1.0.1 - - - - \ No newline at end of file + diff --git a/test-assets/test-projects/HelloWorld/Program.cs b/test-assets/test-projects/HelloWorld/Program.cs index 031164913c..8168c80511 100644 --- a/test-assets/test-projects/HelloWorld/Program.cs +++ b/test-assets/test-projects/HelloWorld/Program.cs @@ -1,12 +1,12 @@ -using System; +using System; -namespace ConsoleApplication +namespace HelloWorld { - public class Program + class Program { - public static void Main(string[] args) + static void Main(string[] args) { Console.WriteLine("Hello World!"); } } -} \ No newline at end of file +} diff --git a/tests/OmniSharp.MSBuild.Tests/MSBuildSelectionTests.cs b/tests/OmniSharp.MSBuild.Tests/MSBuildSelectionTests.cs new file mode 100644 index 0000000000..d527ee4a62 --- /dev/null +++ b/tests/OmniSharp.MSBuild.Tests/MSBuildSelectionTests.cs @@ -0,0 +1,131 @@ +using System; +using OmniSharp.MSBuild.Discovery; +using TestUtility; +using Xunit; +using Xunit.Abstractions; + +namespace OmniSharp.MSBuild.Tests +{ + public class MSBuildSelectionTests : AbstractTestFixture + { + public MSBuildSelectionTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void RegisterDefaultInstanceFindsTheBestInstanceAvailable() + { + var msBuildInstances = new[] + { + GetInvalidMsBuildInstance(), + // Valid + new MSBuildInstance( + "Test Instance", + TestIO.GetRandomTempFolderPath(), + Version.Parse("15.1.2.3"), + DiscoveryType.VisualStudioSetup + ).AddDotNetCoreToFakeInstance(), + GetStandAloneMSBuildInstance() + }; + + var msbuildLocator = new MSFakeLocator(msBuildInstances); + var logger = LoggerFactory.CreateLogger(nameof(RegisterDefaultInstanceFindsTheBestInstanceAvailable)); + + // test + msbuildLocator.RegisterDefaultInstance(logger); + + Assert.NotNull(msbuildLocator.RegisteredInstance); + Assert.Same(msBuildInstances[1], msbuildLocator.RegisteredInstance); + + // clean up + msbuildLocator.DeleteFakeInstancesFolders(); + } + + [Fact] + public void RegisterDefaultInstanceFindsTheBestInstanceAvailableEvenWithOtherValidInstances() + { + var msBuildInstances = new[] + { + new MSBuildInstance( + "Valid Test Instance", + TestIO.GetRandomTempFolderPath(), + Version.Parse("15.3.2.1"), + DiscoveryType.VisualStudioSetup + ), + GetInvalidMsBuildInstance(), + + // Valid + Dotnet Core + new MSBuildInstance( + "Another Valid Test Instance", + TestIO.GetRandomTempFolderPath(), + Version.Parse("15.1.2.3"), + DiscoveryType.VisualStudioSetup + ).AddDotNetCoreToFakeInstance(), + GetStandAloneMSBuildInstance() + }; + + var msbuildLocator = new MSFakeLocator(msBuildInstances); + + var logger = LoggerFactory.CreateLogger( + nameof(RegisterDefaultInstanceFindsTheBestInstanceAvailableEvenWithOtherValidInstances) + ); + + // test + msbuildLocator.RegisterDefaultInstance(logger); + + Assert.NotNull(msbuildLocator.RegisteredInstance); + Assert.Same(msBuildInstances[2], msbuildLocator.RegisteredInstance); + + // clean up + msbuildLocator.DeleteFakeInstancesFolders(); + } + + [Fact] + public void RegisterDefaultInstanceStillPrefersTheFirstInstance() + { + var msBuildInstances = new[] + { + new MSBuildInstance( + "Test Instance", + TestIO.GetRandomTempFolderPath(), + Version.Parse("15.1.2.3"), + DiscoveryType.VisualStudioSetup + ), + GetStandAloneMSBuildInstance() + }; + + var msbuildLocator = new MSFakeLocator(msBuildInstances); + var logger = LoggerFactory.CreateLogger(nameof(RegisterDefaultInstanceStillPrefersTheFirstInstance)); + + // test + msbuildLocator.RegisterDefaultInstance(logger); + + Assert.NotNull(msbuildLocator.RegisteredInstance); + Assert.Same(msBuildInstances[0], msbuildLocator.RegisteredInstance); + + // clean up + msbuildLocator.DeleteFakeInstancesFolders(); + } + + private static MSBuildInstance GetStandAloneMSBuildInstance() + { + return new MSBuildInstance( + "Stand Alone :(", + TestIO.GetRandomTempFolderPath(), + Version.Parse("99.0.0.0"), + DiscoveryType.StandAlone + ); + } + + private static MSBuildInstance GetInvalidMsBuildInstance() + { + return new MSBuildInstance( + "Invalid Instance", + TestIO.GetRandomTempFolderPath(), + Version.Parse("15.0.4.2"), + DiscoveryType.VisualStudioSetup + ); + } + } +} diff --git a/tests/OmniSharp.MSBuild.Tests/ProjectFileInfoTests.cs b/tests/OmniSharp.MSBuild.Tests/ProjectFileInfoTests.cs index 6203cb2e75..c943baec87 100644 --- a/tests/OmniSharp.MSBuild.Tests/ProjectFileInfoTests.cs +++ b/tests/OmniSharp.MSBuild.Tests/ProjectFileInfoTests.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using OmniSharp.MSBuild.Discovery; using OmniSharp.MSBuild.ProjectFile; @@ -32,7 +33,8 @@ private ProjectFileInfo CreateProjectFileInfo(OmniSharpTestHost host, ITestProje loggerFactory: LoggerFactory, sdksPathResolver: sdksPathResolver); - var (projectFileInfo, _, _) = ProjectFileInfo.Load(projectFilePath, loader); + var projectIdInfo = new ProjectIdInfo(ProjectId.CreateNewId(), false); + var (projectFileInfo, _, _) = ProjectFileInfo.Load(projectFilePath, projectIdInfo, loader); return projectFileInfo; } @@ -50,9 +52,9 @@ public async Task HelloWorld_has_correct_property_values() Assert.NotNull(projectFileInfo); Assert.Equal(projectFilePath, projectFileInfo.FilePath); var targetFramework = Assert.Single(projectFileInfo.TargetFrameworks); - Assert.Equal("netcoreapp1.0", targetFramework); - Assert.Equal("bin/Debug/netcoreapp1.0/", projectFileInfo.OutputPath.EnsureForwardSlashes()); - Assert.Equal("obj/Debug/netcoreapp1.0/", projectFileInfo.IntermediateOutputPath.EnsureForwardSlashes()); + Assert.Equal("netcoreapp2.1", targetFramework); + Assert.Equal("bin/Debug/netcoreapp2.1/", projectFileInfo.OutputPath.EnsureForwardSlashes()); + Assert.Equal("obj/Debug/netcoreapp2.1/", projectFileInfo.IntermediateOutputPath.EnsureForwardSlashes()); Assert.Equal(3, projectFileInfo.SourceFiles.Length); // Program.cs, AssemblyInfo.cs, AssemblyAttributes.cs Assert.Equal(LanguageVersion.CSharp7_1, projectFileInfo.LanguageVersion); Assert.Equal("Debug", projectFileInfo.Configuration); @@ -104,5 +106,46 @@ public async Task NetStandardAndNetCoreApp_has_correct_property_values() Assert.Equal("AnyCPU", projectFileInfo.Platform); } } + + [Fact] + public async Task CSharp8AndNullableContext_has_correct_property_values() + { + using (var host = CreateOmniSharpHost()) + using (var testProject = await _testAssets.GetTestProjectAsync("CSharp8AndNullableContext")) + { + var projectFilePath = Path.Combine(testProject.Directory, "CSharp8AndNullableContext.csproj"); + + var projectFileInfo = CreateProjectFileInfo(host, testProject, projectFilePath); + + Assert.NotNull(projectFileInfo); + Assert.Equal(projectFilePath, projectFileInfo.FilePath); + var targetFramework = Assert.Single(projectFileInfo.TargetFrameworks); + Assert.Equal("netcoreapp2.1", targetFramework); + Assert.Equal(LanguageVersion.CSharp8, projectFileInfo.LanguageVersion); + Assert.Equal(NullableContextOptions.Enable, projectFileInfo.NullableContextOptions); + Assert.Equal("Debug", projectFileInfo.Configuration); + Assert.Equal("AnyCPU", projectFileInfo.Platform); + } + } + + [Fact] + public async Task ExternAlias() + { + using (var host = CreateOmniSharpHost()) + using (var testProject = await _testAssets.GetTestProjectAsync("ExternAlias")) + { + var projectFilePath = Path.Combine(testProject.Directory, "ExternAlias.App", "ExternAlias.App.csproj"); + var projectFileInfo = CreateProjectFileInfo(host, testProject, projectFilePath); + Assert.Single(projectFileInfo.ReferenceAliases); + foreach(var kv in projectFileInfo.ReferenceAliases) + { + this.TestOutput.WriteLine($"{kv.Key} = {kv.Value}"); + } + // reference path should be same as evaluated HintPath("$(ProjectDir)../ExternAlias.Lib/bin/Debug/netstandard2.0/ExternAlias.Lib.dll") + var libpath = string.Format($"{Path.Combine(testProject.Directory, "ExternAlias.App")}{Path.DirectorySeparatorChar}../ExternAlias.Lib/bin/Debug/netstandard2.0/ExternAlias.Lib.dll"); + Assert.True(projectFileInfo.ReferenceAliases.ContainsKey(libpath)); + Assert.Equal("abc", projectFileInfo.ReferenceAliases[libpath]); + } + } } } diff --git a/tests/OmniSharp.MSBuild.Tests/ProjectLoadListenerTests.cs b/tests/OmniSharp.MSBuild.Tests/ProjectLoadListenerTests.cs new file mode 100644 index 0000000000..c35449252b --- /dev/null +++ b/tests/OmniSharp.MSBuild.Tests/ProjectLoadListenerTests.cs @@ -0,0 +1,249 @@ +using Microsoft.Build.Construction; +using Microsoft.Build.Execution; +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.Logging; +using OmniSharp.Mef; +using OmniSharp.Models; +using OmniSharp.Models.Events; +using OmniSharp.MSBuild.Notification; +using OmniSharp.Services; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition.Hosting.Core; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using TestUtility; +using Xunit; +using Xunit.Abstractions; + +namespace OmniSharp.MSBuild.Tests +{ + public partial class ProjectLoadListenerTests : AbstractMSBuildTestFixture + { + private VsTfmAndFileExtHashingAlgorithm _tfmAndFileHashingAlgorithm; + private VsReferenceHashingAlgorithm _referenceHashingAlgorithm; + + public ProjectLoadListenerTests(ITestOutputHelper output) : base(output) + { + _tfmAndFileHashingAlgorithm = new VsTfmAndFileExtHashingAlgorithm(); + _referenceHashingAlgorithm = new VsReferenceHashingAlgorithm(); + } + + [Fact] + public void GetTargetFramework_ReturnsTargetFramework() + { + // Arrange + + const string targetFramework = "net461"; + var expectedTFM = GetHashedTargetFramework(targetFramework); + var projectInstance = new ProjectInstance(ProjectRootElement.Create()); + projectInstance.SetProperty(ProjectLoadListener.TargetFramework, targetFramework); + + // Act + var tfm = ProjectLoadListener.GetHashedTargetFrameworks(projectInstance); + + // Assert + Assert.Equal(expectedTFM, tfm.First().Value); + } + + [Fact] + public void GetTargetFramework_NoTFM_ReturnsTargetFrameworkVersion() + { + // Arrange + const string targetFramework = "v4.6.1"; + var expectedTFM = GetHashedTargetFramework(targetFramework); + var projectInstance = new ProjectInstance(ProjectRootElement.Create()); + projectInstance.SetProperty(ProjectLoadListener.TargetFrameworkVersion, targetFramework); + + // Act + var tfm = ProjectLoadListener.GetHashedTargetFrameworks(projectInstance); + + // Assert + Assert.Equal(expectedTFM, tfm.First().Value); + } + + [Fact] + public void GetTargetFramework_PrioritizesTargetFrameworkOverVersion() + { + // Arrange + const string targetFramework = "v4.6.1"; + var expectedTFM = GetHashedTargetFramework(targetFramework); + var projectInstance = new ProjectInstance(ProjectRootElement.Create()); + projectInstance.SetProperty(ProjectLoadListener.TargetFramework, targetFramework); + projectInstance.SetProperty(ProjectLoadListener.TargetFrameworkVersion, "Unexpected"); + + // Act + var tfm = ProjectLoadListener.GetHashedTargetFrameworks(projectInstance); + + // Assert + Assert.Equal(expectedTFM, tfm.First().Value); + } + + [Fact] + public void GetTargetFramework_NoTFM_ReturnsEmpty() + { + // Arrange + var projectInstance = new ProjectInstance(ProjectRootElement.Create()); + + // Act + var tfm = ProjectLoadListener.GetHashedTargetFrameworks(projectInstance); + + // Assert + Assert.Empty(tfm); + } + + [Fact] + public async Task The_target_framework_is_emitted() + { + // Arrange + var expectedTFM = GetHashedTargetFramework("netcoreapp2.1"); + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("HelloWorld")) + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + Assert.Single(messages); + Assert.Equal(messages[0].TargetFrameworks.First(), expectedTFM); + } + } + + [Fact] + public async Task If_there_is_a_solution_file_the_project_guid_present_in_it_is_emitted() + { + // Arrange + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("ProjectAndSolution")) + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + var expectedGuid = "A4C2694D-AEB4-4CB1-8951-5290424EF883".ToLower(); + Assert.Single(messages); + Assert.Equal(messages[0].ProjectGuid, expectedGuid); + } + } + + [Fact] + public async Task If_there_is_no_solution_file_the_hash_of_project_file_content_and_name_is_emitted() + { + // Arrange + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("HelloWorld")) + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + var projectFileContent = File.ReadAllText(Directory.GetFiles(testProject.Directory, "*.csproj").Single()); + var expectedGuid = GetHashedReference($"Filename: HelloWorld.csproj\n{projectFileContent}"); + Assert.Single(messages); + Assert.Equal(messages[0].ProjectGuid, expectedGuid); + } + } + + [Fact] + public async Task Given_a_restored_project_the_references_are_emitted() + { + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("HelloWorld")) + { + var dotnetCliService = new DotNetCliService(LoggerFactory, emitter); + await dotnetCliService.RestoreAsync(testProject.Directory); + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + Assert.Single(messages); + Assert.NotEmpty(messages[0].References.Where(reference => reference == GetHashedReference("system.core"))); + } + } + } + + + [Fact] + public async Task If_there_are_multiple_target_frameworks_they_are_emitted() + { + // Arrange + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("ProjectWithMultiTFMLib/Lib")) + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + Assert.Single(messages); + var tfm = messages[0].TargetFrameworks.ToArray(); + Assert.Equal(2, tfm.Count()); + Assert.Equal(tfm[0], GetHashedTargetFramework("netstandard1.3")); + Assert.Equal(tfm[1], GetHashedTargetFramework("netstandard2.0")); + } + } + + [Fact] + public async Task The_hashed_references_of_the_source_files_are_emitted() + { + // Arrange + var messages = new List(); + var emitter = new ProjectLoadTestEventEmitter(messages); + + var listener = new ProjectLoadListener(LoggerFactory, emitter); + var exports = new ExportDescriptorProvider[] + { + MefValueProvider.From(listener) + }; + + using (var testProject = await TestAssets.Instance.GetTestProjectAsync("HelloWorld")) + using (var host = CreateMSBuildTestHost(testProject.Directory, exports)) + { + Assert.Single(messages); + Assert.Single(messages[0].FileExtensions); + Assert.Equal(messages[0].FileExtensions.First(), GetHashedFileExtension(".cs")); + } + } + + private string GetHashedTargetFramework(string targetFramework) + { + return _tfmAndFileHashingAlgorithm.HashInput(targetFramework).Value; + } + + private string GetHashedFileExtension(string fileExtension) + { + return _tfmAndFileHashingAlgorithm.HashInput(fileExtension).Value; + } + private string GetHashedReference(string reference) + { + return _referenceHashingAlgorithm.HashInput(reference).Value; + } + } +} diff --git a/tests/OmniSharp.MSBuild.Tests/ProjectLoadTestEventEmitter.cs b/tests/OmniSharp.MSBuild.Tests/ProjectLoadTestEventEmitter.cs new file mode 100644 index 0000000000..833ce81c07 --- /dev/null +++ b/tests/OmniSharp.MSBuild.Tests/ProjectLoadTestEventEmitter.cs @@ -0,0 +1,27 @@ +using OmniSharp.Eventing; +using OmniSharp.Models.Events; +using System.Collections.Generic; + +namespace OmniSharp.MSBuild.Tests +{ + public partial class ProjectLoadListenerTests + { + public class ProjectLoadTestEventEmitter : IEventEmitter + { + private readonly IList _messages; + + public ProjectLoadTestEventEmitter(IList messages) + { + _messages = messages; + } + + public void Emit(string kind, object args) + { + if(args is ProjectConfigurationMessage) + { + _messages.Add((ProjectConfigurationMessage)args); + } + } + } + } +} diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/DiagnosticsV2Facts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/DiagnosticsV2Facts.cs index 08d0d76d5c..5a0701b59e 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/DiagnosticsV2Facts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/DiagnosticsV2Facts.cs @@ -17,7 +17,7 @@ public DiagnosticsV2Facts(ITestOutputHelper output, SharedOmniSharpHostFixture s { } - [Theory] + [Theory(Skip = "Test needs to be updated for service changes")] [InlineData("a.cs")] [InlineData("a.csx")] public async Task CodeCheckSpecifiedFileOnly(string filename) @@ -45,7 +45,7 @@ public async Task CodeCheckSpecifiedFileOnly(string filename) Assert.Equal(filename, result.FileName); } - [Theory] + [Theory(Skip = "Test needs to be updated for service changes")] [InlineData("a.cs", "b.cs")] [InlineData("a.csx", "b.csx")] public async Task CheckAllFiles(string filename1, string filename2) @@ -77,7 +77,7 @@ public async Task CheckAllFiles(string filename1, string filename2) Assert.Equal(filename2, b.FileName); } - [Theory] + [Theory(Skip = "Test needs to be updated for service changes")] [InlineData("a.cs", "b.cs")] [InlineData("a.csx", "b.csx")] public async Task EnablesWhenEndPointIsHit(string filename1, string filename2) diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FixUsingsFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FixUsingsFacts.cs index 88f9471832..43f58c1812 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FixUsingsFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FixUsingsFacts.cs @@ -250,7 +250,7 @@ public method1() Line = point.Line, Column = point.Offset, FileName = TestFileName, - Text = "`classX` is ambiguous" + Text = "`classX` is ambiguous. Namespaces: using nsA; using nsB;", } }; diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/HighlightFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/HighlightFacts.cs index 026f7d042a..232d506e87 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/HighlightFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/HighlightFacts.cs @@ -56,7 +56,7 @@ class C1 { int n = true; } AssertSyntax(highlights, testFile.Content.Code, 0, Keyword("namespace"), - Identifier("N1"), + NamespaceName("N1"), Punctuation("{"), Keyword("class"), ClassName("C1"), @@ -236,6 +236,7 @@ private static void AssertSyntax(HighlightSpan[] highlights, string code, int st private static (string kind, string text) ClassName(string text) => ("class name", text); private static (string kind, string text) Field(string text) => ("field name", text); private static (string kind, string text) Identifier(string text) => ("identifier", text); + private static (string kind, string text) NamespaceName(string text) => ("namespace name", text); private static (string kind, string text) Keyword(string text) => ("keyword", text); private static (string kind, string text) Number(string text) => ("number", text); private static (string kind, string text) Operator(string text) => ("operator", text); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/IntellisenseFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/IntellisenseFacts.cs index 6a251adfae..f1985ebd9a 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/IntellisenseFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/IntellisenseFacts.cs @@ -282,7 +282,7 @@ public MyClass2() "; var completions = await FindCompletionsAsync(filename, source); - ContainsCompletions(completions.Select(c => c.CompletionText).Take(1), "text:"); + ContainsCompletions(completions.Select(c => c.CompletionText).Take(1), "text"); } [Theory] diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/TypeLookupFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/TypeLookupFacts.cs index 47ce7a5757..6da9517a0c 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/TypeLookupFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/TypeLookupFacts.cs @@ -147,6 +147,8 @@ public Foo() { public IDictionary> SomeDict { get; } public void Compute(int index = 2) { } + + private const int foo = 1; } } @@ -154,6 +156,13 @@ namespace Bar2 { class Foo2 { } } + + namespace Bar3 { + enum Foo3 { + Val1 = 1, + Val2 + } + } "); [Fact] @@ -234,6 +243,20 @@ public async Task DisplayFormatFor_FieldSymbol() Assert.Equal("Foo2 Foo._someField", response.Type); } + [Fact] + public async Task DisplayFormatFor_FieldSymbol_WithConstantValue() + { + var response = await GetTypeLookUpResponse(line: 19, column: 41); + Assert.Equal("int Foo.foo = 1", response.Type); + } + + [Fact] + public async Task DisplayFormatFor_EnumValue() + { + var response = await GetTypeLookUpResponse(line: 31, column: 23); + Assert.Equal("Foo3.Val2 = 2", response.Type); + } + [Fact] public async Task DisplayFormatFor_PropertySymbol() { diff --git a/tests/OmniSharp.Stdio.Tests/StdioServerFacts.cs b/tests/OmniSharp.Stdio.Tests/StdioServerFacts.cs index 00c8d55425..bfe751e74d 100644 --- a/tests/OmniSharp.Stdio.Tests/StdioServerFacts.cs +++ b/tests/OmniSharp.Stdio.Tests/StdioServerFacts.cs @@ -5,9 +5,8 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using OmniSharp.Eventing; +using OmniSharp.Protocol; using OmniSharp.Services; -using OmniSharp.Stdio.Protocol; -using OmniSharp.Stdio.Services; using Xunit; namespace OmniSharp.Stdio.Tests diff --git a/tests/OmniSharp.Stdio.Tests/TestTextWriter.cs b/tests/OmniSharp.Stdio.Tests/TestTextWriter.cs index 20388e50b1..124726f385 100644 --- a/tests/OmniSharp.Stdio.Tests/TestTextWriter.cs +++ b/tests/OmniSharp.Stdio.Tests/TestTextWriter.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; namespace OmniSharp.Stdio.Tests { @@ -44,4 +44,4 @@ public Task WriteLineAsync(object value) return Task.Factory.StartNew(() => WriteLine(value)); } } -} \ No newline at end of file +} diff --git a/tests/OmniSharp.Tests/UpdateBufferFilterFacts.cs b/tests/OmniSharp.Tests/UpdateBufferFilterFacts.cs index c3afc6f632..66d8d6fa4d 100644 --- a/tests/OmniSharp.Tests/UpdateBufferFilterFacts.cs +++ b/tests/OmniSharp.Tests/UpdateBufferFilterFacts.cs @@ -6,6 +6,8 @@ using TestUtility; using Xunit; using Xunit.Abstractions; +using System; +using System.Threading; namespace OmniSharp.Tests { @@ -70,7 +72,7 @@ public async Task UpdateBuffer_AddsNewDocumentsIfNeeded() } } - [Fact(Skip = "Fails on line 95 because there are 3 documents, not 2, named 'transient.cs'")] + [Fact] public async Task UpdateBuffer_TransientDocumentsDisappearWhenProjectAddsThem() { var testFile = new TestFile("test.cs", "class C {}"); @@ -88,11 +90,29 @@ public async Task UpdateBuffer_TransientDocumentsDisappearWhenProjectAddsThem() loader: TextLoader.From(TextAndVersion.Create(SourceText.From("enum E{}"), VersionStamp.Create())), filePath: "transient.cs"); + // apply changes - this raises workspaceChanged event asynchronously. var newSolution = host.Workspace.CurrentSolution.AddDocument(document); host.Workspace.TryApplyChanges(newSolution); - docIds = host.Workspace.CurrentSolution.GetDocumentIdsWithFilePath("transient.cs"); - Assert.Equal(2, docIds.Length); + // wait for workspaceChange event to be raised. + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(10)); + while (!cts.IsCancellationRequested) + { + await Task.Yield(); + docIds = host.Workspace.CurrentSolution.GetDocumentIdsWithFilePath("transient.cs"); + if (docIds.Count() <= 1) break; + } + + // assert that only one document with "transient.cs" filePath remains + Assert.Single(docIds); + + // assert that the remaining document is part of project1 + project1 = host.Workspace.CurrentSolution.GetProject(project1.Id); + Assert.Contains(docIds[0], project1.DocumentIds); + + // assert that the remaining document is not transient. + Assert.False(host.Workspace.BufferManager.IsTransientDocument(docIds[0])); await host.Workspace.BufferManager.UpdateBufferAsync(new Request() { FileName = "transient.cs", Buffer = "enum E {}" }); var sourceText = await host.Workspace.CurrentSolution.GetDocument(docIds.First()).GetTextAsync(); diff --git a/tests/TestUtility/MSFakeLocator.cs b/tests/TestUtility/MSFakeLocator.cs new file mode 100644 index 0000000000..41ca907b41 --- /dev/null +++ b/tests/TestUtility/MSFakeLocator.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using OmniSharp.MSBuild.Discovery; + +namespace TestUtility +{ + public class MSFakeLocator : IMSBuildLocator + { + private readonly ImmutableArray _instances; + + public MSBuildInstance RegisteredInstance { get; private set; } + + public MSFakeLocator(IEnumerable instances) + { + _instances = instances.ToImmutableArray(); + } + + public void RegisterInstance(MSBuildInstance instance) + => RegisteredInstance = instance; + + public ImmutableArray GetInstances() + => _instances; + + public void DeleteFakeInstancesFolders() + { + foreach (var instance in _instances) + { + if (Directory.Exists(instance.MSBuildPath)) + Directory.Delete(instance.MSBuildPath, true); + } + } + } +} diff --git a/tests/TestUtility/TestFolder.cs b/tests/TestUtility/TestFolder.cs new file mode 100644 index 0000000000..7517c96e4f --- /dev/null +++ b/tests/TestUtility/TestFolder.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace TestUtility +{ + public static class TestIO + { + public static string GetRandomTempFolderPath() + { + var path = Path.Combine( + Path.GetTempPath(), + Path.GetRandomFileName() + ); + + Directory.CreateDirectory(path); + + return path; + } + + public static void TouchFakeFile(string path) + { + File.WriteAllText(path, "just testing :)"); + } + } +} diff --git a/tests/TestUtility/TestHelpers.cs b/tests/TestUtility/TestHelpers.cs index a2f4c34a75..1820c81fa0 100644 --- a/tests/TestUtility/TestHelpers.cs +++ b/tests/TestUtility/TestHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; @@ -7,6 +8,7 @@ using Microsoft.Extensions.Logging; using OmniSharp; using OmniSharp.FileWatching; +using OmniSharp.MSBuild.Discovery; using OmniSharp.Script; using OmniSharp.Services; @@ -24,7 +26,7 @@ public static OmniSharpWorkspace CreateCsxWorkspace(TestFile testFile) public static void AddCsxProjectToWorkspace(OmniSharpWorkspace workspace, TestFile testFile) { var references = GetReferences(); - var scriptHelper = new ScriptProjectProvider(new ScriptOptions(), new OmniSharpEnvironment(), new LoggerFactory(), true); + var scriptHelper = new ScriptProjectProvider(new ScriptOptions(), new OmniSharpEnvironment(), new LoggerFactory(), true); var project = scriptHelper.CreateProject(testFile.FileName, references.Union(new[] { MetadataReference.CreateFromFile(typeof(CommandLineScriptGlobals).GetTypeInfo().Assembly.Location) }), testFile.FileName, typeof(CommandLineScriptGlobals), Enumerable.Empty()); workspace.AddProject(project); @@ -91,5 +93,22 @@ private static IEnumerable GetReferences() return references; } + + public static MSBuildInstance AddDotNetCoreToFakeInstance(this MSBuildInstance instance) + { + const string dotnetSdkResolver = "Microsoft.DotNet.MSBuildSdkResolver"; + + var directory = Path.Combine( + instance.MSBuildPath, + "SdkResolvers", + dotnetSdkResolver + ); + + Directory.CreateDirectory(directory); + + TestIO.TouchFakeFile(Path.Combine(directory, dotnetSdkResolver + ".dll")); + + return instance; + } } } diff --git a/tests/TestUtility/TestServiceProvider.cs b/tests/TestUtility/TestServiceProvider.cs index e13242ba37..15efceda42 100644 --- a/tests/TestUtility/TestServiceProvider.cs +++ b/tests/TestUtility/TestServiceProvider.cs @@ -11,7 +11,6 @@ using OmniSharp.MSBuild.Discovery; using OmniSharp.Options; using OmniSharp.Services; -using OmniSharp.Stdio.Services; using OmniSharp.Utilities; using TestUtility.Logging; using Xunit.Abstractions; diff --git a/tests/TestUtility/TestSharedTextWriter.cs b/tests/TestUtility/TestSharedTextWriter.cs index 3433e79897..c61c05f11d 100644 --- a/tests/TestUtility/TestSharedTextWriter.cs +++ b/tests/TestUtility/TestSharedTextWriter.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using OmniSharp.Stdio.Services; +using OmniSharp.Services; using Xunit.Abstractions; namespace TestUtility diff --git a/tests/TestUtility/TestUtility.csproj b/tests/TestUtility/TestUtility.csproj index 14de90b483..5828ebd8df 100644 --- a/tests/TestUtility/TestUtility.csproj +++ b/tests/TestUtility/TestUtility.csproj @@ -13,6 +13,7 @@ + diff --git a/tests/app.config b/tests/app.config index 34fb8c206a..45e6a8ef7e 100644 --- a/tests/app.config +++ b/tests/app.config @@ -7,15 +7,15 @@ - + - + - +