diff --git a/Roslyn.sln b/Roslyn.sln index 7a24e77b16147..924423c8aba59 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysisTest", "src\Compilers\Core\CodeAnalysisTest\CodeAnalysisTest.csproj", "{A4C99B85-765C-4C65-9C2A-BB609AAB09E6}" EndProject @@ -363,6 +363,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerformanceTesting", "src\T EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Roslyn", "src\Deployment\Roslyn.csproj", "{600AF682-E097-407B-AD85-EE3CED37E680}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EditorFeatures.Next", "src\EditorFeatures\Next\EditorFeatures.Next.csproj", "{366BBCDC-B05F-4677-9B5B-78BA816A1484}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualStudioSetup.Next", "src\VisualStudio\Setup.Next\VisualStudioSetup.Next.csproj", "{143FE684-6E1C-41DF-9C60-84C7772DC49C}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Test\Utilities\Shared\TestUtilities.projitems*{76c6f005-c89d-4348-bb4a-391898dbeb52}*SharedItemsImports = 4 @@ -395,8 +399,8 @@ Global src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d3}*SharedItemsImports = 4 src\Compilers\Core\SharedCollections\SharedCollections.projitems*{afde6bea-5038-4a4a-a88e-dbd2e4088eed}*SharedItemsImports = 4 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{fa0e905d-ec46-466d-b7b2-3b5557f9428c}*SharedItemsImports = 4 - src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4 src\Compilers\Core\SharedCollections\SharedCollections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4 + src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4 src\Compilers\Core\MSBuildTask\Shared\MSBuildTask.Shared.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 4 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 4 @@ -3130,6 +3134,46 @@ Global {600AF682-E097-407B-AD85-EE3CED37E680}.Release|x64.Build.0 = Release|Any CPU {600AF682-E097-407B-AD85-EE3CED37E680}.Release|x86.ActiveCfg = Release|Any CPU {600AF682-E097-407B-AD85-EE3CED37E680}.Release|x86.Build.0 = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|ARM.ActiveCfg = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|ARM.Build.0 = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|x64.ActiveCfg = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|x64.Build.0 = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|x86.ActiveCfg = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Debug|x86.Build.0 = Debug|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|Any CPU.Build.0 = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|ARM.ActiveCfg = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|ARM.Build.0 = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|x64.ActiveCfg = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|x64.Build.0 = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|x86.ActiveCfg = Release|Any CPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484}.Release|x86.Build.0 = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|ARM.Build.0 = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|x64.ActiveCfg = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|x64.Build.0 = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|x86.ActiveCfg = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Debug|x86.Build.0 = Debug|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|Any CPU.Build.0 = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|ARM.ActiveCfg = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|ARM.Build.0 = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|x64.ActiveCfg = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|x64.Build.0 = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|x86.ActiveCfg = Release|Any CPU + {143FE684-6E1C-41DF-9C60-84C7772DC49C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3296,5 +3340,7 @@ Global {A32EAB7F-691C-4D00-98C4-F50C37BB4754} = {C2D1346B-9665-4150-B644-075CF1636BAA} {A57DDFE5-AB0E-4371-98E5-11B9218DF11C} = {C2D1346B-9665-4150-B644-075CF1636BAA} {DA0D2A70-A2F9-4654-A99A-3227EDF54FF1} = {CAD2965A-19AB-489F-BE2E-7649957F914A} + {366BBCDC-B05F-4677-9B5B-78BA816A1484} = {EE97CB90-33BB-4F3A-9B3D-69375DEC6AC6} + {143FE684-6E1C-41DF-9C60-84C7772DC49C} = {8DBA5174-B0AA-4561-82B1-A46607697753} EndGlobalSection EndGlobal diff --git a/build/Targets/VSL.Imports.targets b/build/Targets/VSL.Imports.targets index 5d1f95b1e087d..cb4ee932e0930 100644 --- a/build/Targets/VSL.Imports.targets +++ b/build/Targets/VSL.Imports.targets @@ -82,6 +82,38 @@ + + + + $(VisualStudioReferenceAssemblyVersion) + + + $(VisualStudioReferenceAssemblyVersion) + + + $(VisualStudioReferenceAssemblyVersion) + + + + true + true + + + + + + + true + + + @@ -94,7 +126,7 @@ - + @@ -112,13 +144,13 @@ - + - + diff --git a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs index 2e275d810df80..f2d3d02a1a5e8 100644 --- a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs @@ -25,18 +25,8 @@ public class IntegrationTests : TestBase static IntegrationTests() { - using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0", writable: false)) - { - if (key != null) - { - var toolsPath = key.GetValue("MSBuildToolsPath"); - if (toolsPath != null) - { - s_msbuildDirectory = toolsPath.ToString(); - s_msbuildExecutable = Path.Combine(s_msbuildDirectory, "MSBuild.exe"); - } - } - } + s_msbuildDirectory = TestHelpers.GetMSBuildDirectory(); + s_msbuildExecutable = Path.Combine(s_msbuildDirectory, "MSBuild.exe"); } private readonly TempDirectory _tempDirectory; diff --git a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj index 6a42a77fbfe82..dab9e06ca6d92 100644 --- a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj +++ b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj @@ -1,6 +1,6 @@  - + @@ -16,6 +16,7 @@ ..\..\..\..\ true v4.6 + diff --git a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs index a7f32443622e9..40c444b592a8f 100644 --- a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs +++ b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -8,14 +10,14 @@ namespace Microsoft.CodeAnalysis.Collections { /// - /// A MultiDictionary that allows only adding, and - /// preserves the order of values added to the dictionary. - /// Thread-safe for reading, but not for adding. + /// A MultiDictionary that allows only adding, and preserves the order of values added to the + /// dictionary. Thread-safe for reading, but not for adding. /// /// /// Always uses the default comparer. /// - internal sealed class OrderPreservingMultiDictionary + internal sealed class OrderPreservingMultiDictionary : + IEnumerable.ValueSet>> { #region Pooling @@ -30,6 +32,12 @@ public void Free() { if (_dictionary != null) { + // Allow our ValueSets to return their underlying ArrayBuilders to the pool. + foreach (var kvp in _dictionary) + { + kvp.Value.Free(); + } + _dictionary.Free(); _dictionary = null; } @@ -57,159 +65,231 @@ public void Free() #endregion Pooling + // An empty dictionary we keep around to simplify certain operations (like "Keys") + // when we don't have an underlying dictionary of our own. + private static readonly Dictionary s_emptyDictionary = new Dictionary(); + + // The underlying dictionary we store our data in. null if we are empty. + private PooledDictionary _dictionary; + public OrderPreservingMultiDictionary() { } - // store either a single V or an ArrayBuilder - /// - /// Each value is either a single V or an . - /// Null when the dictionary is empty. - /// Don't access the field directly. - /// - private PooledDictionary _dictionary; - private void EnsureDictionary() { - _dictionary = _dictionary ?? PooledDictionary.GetInstance(); + _dictionary = _dictionary ?? PooledDictionary.GetInstance(); } - public bool IsEmpty - { - get { return _dictionary == null; } - } + public bool IsEmpty => _dictionary == null; /// /// Add a value to the dictionary. /// public void Add(K k, V v) { - object item; - if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) + ValueSet valueSet; + if (!this.IsEmpty && _dictionary.TryGetValue(k, out valueSet)) { - var arrayBuilder = item as ArrayBuilder; - if (arrayBuilder == null) - { - // Promote from singleton V to ArrayBuilder. - Debug.Assert(item is V, "Item must be either a V or an ArrayBuilder"); - arrayBuilder = new ArrayBuilder(2); - arrayBuilder.Add((V)item); - arrayBuilder.Add(v); - _dictionary[k] = arrayBuilder; - } - else - { - arrayBuilder.Add(v); - } + Debug.Assert(valueSet.Count >= 1); + // Have to re-store the ValueSet in case we upgraded the existing ValueSet from + // holding a single item to holding multiple items. + _dictionary[k] = valueSet.WithAddedItem(v); } else { this.EnsureDictionary(); - _dictionary[k] = v; + _dictionary[k] = new ValueSet(v); } } - /// - /// Add multiple values to the dictionary. - /// - public void AddRange(K k, ImmutableArray values) + public Dictionary.Enumerator GetEnumerator() { - if (values.IsEmpty) - return; - - object item; - ArrayBuilder arrayBuilder; + return IsEmpty ? s_emptyDictionary.GetEnumerator() : _dictionary.GetEnumerator(); + } - if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) - { - arrayBuilder = item as ArrayBuilder; - if (arrayBuilder == null) - { - // Promote from singleton V to ArrayBuilder. - Debug.Assert(item is V, "Item must be either a V or an ArrayBuilder"); - arrayBuilder = new ArrayBuilder(1 + values.Length); - arrayBuilder.Add((V)item); - arrayBuilder.AddRange(values); - _dictionary[k] = arrayBuilder; - } - else - { - arrayBuilder.AddRange(values); - } - } - else - { - this.EnsureDictionary(); + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } - if (values.Length == 1) - { - _dictionary[k] = values[0]; - } - else - { - arrayBuilder = new ArrayBuilder(values.Length); - arrayBuilder.AddRange(values); - _dictionary[k] = arrayBuilder; - } - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } /// - /// Get the number of values associated with a key. + /// Get all values associated with K, in the order they were added. + /// Returns empty read-only array if no values were present. /// - public int GetCountForKey(K k) + public ImmutableArray this[K k] { - object item; - if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) + get { - return (item as ArrayBuilder)?.Count ?? 1; - } + ValueSet valueSet; + if (!this.IsEmpty && _dictionary.TryGetValue(k, out valueSet)) + { + Debug.Assert(valueSet.Count >= 1); + return valueSet.Items; + } - return 0; + return ImmutableArray.Empty; + } } /// - /// Returns true if one or more items with given key have been added. + /// Get a collection of all the keys. /// - public bool ContainsKey(K k) + public Dictionary.KeyCollection Keys { - return !this.IsEmpty && _dictionary.ContainsKey(k); + get { return this.IsEmpty ? s_emptyDictionary.Keys : _dictionary.Keys; } } - /// - /// Get all values associated with K, in the order they were added. - /// Returns empty read-only array if no values were present. - /// - public ImmutableArray this[K k] + public struct ValueSet : IEnumerable { - get + /// + /// Each value is either a single V or an . + /// Never null. + /// + private readonly object _value; + + internal ValueSet(V value) { - object item; - if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) + _value = value; + } + + internal ValueSet(ArrayBuilder values) + { + _value = values; + } + + internal void Free() + { + var arrayBuilder = _value as ArrayBuilder; + arrayBuilder?.Free(); + } + + internal V this[int index] + { + get { - var arrayBuilder = item as ArrayBuilder; + Debug.Assert(this.Count >= 1); + + var arrayBuilder = _value as ArrayBuilder; + if (arrayBuilder == null) + { + if (index == 0) + { + return (V)_value; + } + else + { + throw new IndexOutOfRangeException(); + } + } + else + { + return arrayBuilder[index]; + } + } + } + + internal ImmutableArray Items + { + get + { + Debug.Assert(this.Count >= 1); + + var arrayBuilder = _value as ArrayBuilder; if (arrayBuilder == null) { // promote singleton to set - Debug.Assert(item is V, "Item must be either a V or an ArrayBuilder"); - return ImmutableArray.Create((V)item); + Debug.Assert(_value is V, "Item must be a a V"); + return ImmutableArray.Create((V)_value); } else { return arrayBuilder.ToImmutable(); } } + } - return ImmutableArray.Empty; + internal int Count => (_value as ArrayBuilder)?.Count ?? 1; + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } - } - /// - /// Get a collection of all the keys. - /// - public ICollection Keys - { - get { return this.IsEmpty ? SpecializedCollections.EmptyCollection() : _dictionary.Keys; } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + internal ValueSet WithAddedItem(V item) + { + Debug.Assert(this.Count >= 1); + + var arrayBuilder = _value as ArrayBuilder; + if (arrayBuilder == null) + { + // Promote from singleton V to ArrayBuilder. + Debug.Assert(_value is V, "_value must be a V"); + + // By default we allocate array builders with a size of two. That's to store + // the single item already in _value, and to store the item we're adding. + // In general, we presume that the amount of values per key will be low, so this + // means we have very little overhead when there are multiple keys per value. + arrayBuilder = ArrayBuilder.GetInstance(capacity: 2); + arrayBuilder.Add((V)_value); + arrayBuilder.Add(item); + } + else + { + arrayBuilder.Add(item); + } + + return new ValueSet(arrayBuilder); + } + + public struct Enumerator : IEnumerator + { + private readonly ValueSet _valueSet; + private readonly int _count; + private int _index; + + public Enumerator(ValueSet valueSet) + { + _valueSet = valueSet; + _count = _valueSet.Count; + Debug.Assert(_count >= 1); + _index = -1; + } + + public V Current => _valueSet[_index]; + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + _index++; + return _index < _count; + } + + public void Reset() + { + _index = -1; + } + + public void Dispose() + { + } + } } } -} +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Compilation/IExpression.cs b/src/Compilers/Core/Portable/Compilation/IExpression.cs index 98eb2db52eb24..4cf6988a2218f 100644 --- a/src/Compilers/Core/Portable/Compilation/IExpression.cs +++ b/src/Compilers/Core/Portable/Compilation/IExpression.cs @@ -4,6 +4,10 @@ namespace Microsoft.CodeAnalysis.Semantics { + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IHasArgumentsExpression : IOperation { /// @@ -23,6 +27,10 @@ public interface IHasArgumentsExpression : IOperation /// /// Represents a C# or VB method invocation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IInvocationExpression : IHasArgumentsExpression { /// @@ -48,6 +56,10 @@ public interface IInvocationExpression : IHasArgumentsExpression /// /// Represents an argument in a method invocation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IArgument : IOperation { /// @@ -100,6 +112,10 @@ public enum ArgumentKind /// /// Represents a reference to an array element. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IArrayElementReferenceExpression : IOperation { /// @@ -115,6 +131,10 @@ public interface IArrayElementReferenceExpression : IOperation /// /// Represents a reference through a pointer. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IPointerIndirectionReferenceExpression : IOperation { /// @@ -126,6 +146,10 @@ public interface IPointerIndirectionReferenceExpression : IOperation /// /// Represents a reference to a declared local variable. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILocalReferenceExpression : IOperation { /// @@ -137,6 +161,10 @@ public interface ILocalReferenceExpression : IOperation /// /// Represents a reference to a parameter. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IParameterReferenceExpression : IOperation { /// @@ -148,6 +176,10 @@ public interface IParameterReferenceExpression : IOperation /// /// Represents a reference to a local variable synthesized by language analysis. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISyntheticLocalReferenceExpression : IOperation { /// @@ -180,6 +212,10 @@ public enum SyntheticLocalKind /// /// Represents a C# this or base expression, or a VB Me, MyClass, or MyBase expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IInstanceReferenceExpression : IOperation { /// @@ -201,10 +237,14 @@ public enum InstanceReferenceKind /// Indicates an explicit MyClass expression. ThisClass = 0x4 } - + /// /// Represents a reference to a member of a class, struct, or interface. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IMemberReferenceExpression : IOperation { /// @@ -213,14 +253,18 @@ public interface IMemberReferenceExpression : IOperation IOperation Instance { get; } /// - /// Referenced member. - /// + /// Referenced member. + /// ISymbol Member { get; } } /// /// Represents a reference to a field. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IFieldReferenceExpression : IMemberReferenceExpression { /// @@ -232,6 +276,10 @@ public interface IFieldReferenceExpression : IMemberReferenceExpression /// /// Represents a reference to a method other than as the target of an invocation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IMethodBindingExpression : IMemberReferenceExpression { /// @@ -248,6 +296,10 @@ public interface IMethodBindingExpression : IMemberReferenceExpression /// /// Represents a reference to a property. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IPropertyReferenceExpression : IMemberReferenceExpression { /// @@ -259,6 +311,10 @@ public interface IPropertyReferenceExpression : IMemberReferenceExpression /// /// Represents a reference to an indexed property. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IIndexedPropertyReferenceExpression : IPropertyReferenceExpression, IHasArgumentsExpression { } @@ -266,6 +322,10 @@ public interface IIndexedPropertyReferenceExpression : IPropertyReferenceExpress /// /// Represents a reference to an event. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IEventReferenceExpression : IMemberReferenceExpression { /// @@ -277,6 +337,10 @@ public interface IEventReferenceExpression : IMemberReferenceExpression /// /// Represents a binding of an event. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IEventAssignmentExpression : IOperation { /// @@ -303,6 +367,10 @@ public interface IEventAssignmentExpression : IOperation /// /// Represents an expression that includes a ? or ?. conditional access instance expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IConditionalAccessExpression : IOperation { /// @@ -318,6 +386,10 @@ public interface IConditionalAccessExpression : IOperation /// /// Represents the value of a conditionally-accessed expression within an expression containing a conditional access. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IConditionalAccessInstanceExpression : IOperation { } @@ -326,6 +398,10 @@ public interface IConditionalAccessInstanceExpression : IOperation /// Represents a general placeholder when no more specific kind of placeholder is available. /// A placeholder is an expression whose meaning is inferred from context. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IPlaceholderExpression : IOperation { } @@ -333,6 +409,10 @@ public interface IPlaceholderExpression : IOperation /// /// Represents a unary, binary, relational, or conversion operation that can use an operator method. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IHasOperatorMethodExpression : IOperation { /// @@ -348,6 +428,10 @@ public interface IHasOperatorMethodExpression : IOperation /// /// Represents an operation with one operand. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IUnaryOperatorExpression : IHasOperatorMethodExpression { /// @@ -473,10 +557,13 @@ public enum UnaryOperationKind Invalid = UnaryOperandKind.Invalid | SimpleUnaryOperationKind.Invalid } - /// /// Represents an operation with two operands that produces a result with the same type as at least one of the operands. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IBinaryOperatorExpression : IHasOperatorMethodExpression { /// @@ -831,6 +918,10 @@ public static BinaryOperandsKind GetBinaryOperandsKind(BinaryOperationKind kind) /// /// Represents a conversion operation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IConversionExpression : IHasOperatorMethodExpression { /// @@ -882,6 +973,10 @@ public enum ConversionKind /// /// Represents a C# ?: or VB If expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IConditionalChoiceExpression : IOperation { /// @@ -901,6 +996,10 @@ public interface IConditionalChoiceExpression : IOperation /// /// Represents a null-coalescing expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface INullCoalescingExpression : IOperation { /// @@ -916,6 +1015,10 @@ public interface INullCoalescingExpression : IOperation /// /// Represents an expression that tests if a value is of a specific type. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IIsTypeExpression : IOperation { /// @@ -931,6 +1034,10 @@ public interface IIsTypeExpression : IOperation /// /// Represents an expression operating on a type. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ITypeOperationExpression : IOperation { /// @@ -942,6 +1049,10 @@ public interface ITypeOperationExpression : IOperation /// /// Represents a SizeOf expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISizeOfExpression : ITypeOperationExpression { } @@ -949,6 +1060,10 @@ public interface ISizeOfExpression : ITypeOperationExpression /// /// Represents a TypeOf expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ITypeOfExpression : ITypeOperationExpression { } @@ -956,6 +1071,10 @@ public interface ITypeOfExpression : ITypeOperationExpression /// /// Represents a lambda expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILambdaExpression : IOperation { /// @@ -971,6 +1090,10 @@ public interface ILambdaExpression : IOperation /// /// Represents a textual literal numeric, string, etc. expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILiteralExpression : IOperation { /// @@ -982,6 +1105,10 @@ public interface ILiteralExpression : IOperation /// /// Represents an await expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IAwaitExpression : IOperation { /// @@ -993,6 +1120,10 @@ public interface IAwaitExpression : IOperation /// /// Represents an expression that creates a pointer value by taking the address of a reference. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IAddressOfExpression : IOperation { /// @@ -1004,6 +1135,10 @@ public interface IAddressOfExpression : IOperation /// /// Represents a new/New expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IObjectCreationExpression : IHasArgumentsExpression { /// @@ -1019,6 +1154,10 @@ public interface IObjectCreationExpression : IHasArgumentsExpression /// /// Represents an initializer for a field, property, or parameter. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISymbolInitializer : IOperation { IOperation Value { get; } @@ -1027,6 +1166,10 @@ public interface ISymbolInitializer : IOperation /// /// Represents an initialization of a field. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IFieldInitializer : ISymbolInitializer { /// @@ -1038,6 +1181,10 @@ public interface IFieldInitializer : ISymbolInitializer /// /// Represents an initialization of a property. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IPropertyInitializer : ISymbolInitializer { /// @@ -1049,6 +1196,10 @@ public interface IPropertyInitializer : ISymbolInitializer /// /// Represents an initialization of a parameter at the point of declaration. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IParameterInitializer : ISymbolInitializer { /// @@ -1060,6 +1211,10 @@ public interface IParameterInitializer : ISymbolInitializer /// /// Represents the creation of an array instance. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IArrayCreationExpression : IOperation { /// @@ -1079,6 +1234,10 @@ public interface IArrayCreationExpression : IOperation /// /// Represents the initialization of an array instance. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IArrayInitializer : IOperation { /// @@ -1090,6 +1249,10 @@ public interface IArrayInitializer : IOperation /// /// Represents an assignment expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IAssignmentExpression : IOperation { /// @@ -1105,6 +1268,10 @@ public interface IAssignmentExpression : IOperation /// /// Represents an assignment expression that includes a binary operation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ICompoundAssignmentExpression : IAssignmentExpression, IHasOperatorMethodExpression { /// @@ -1116,6 +1283,10 @@ public interface ICompoundAssignmentExpression : IAssignmentExpression, IHasOper /// /// Represents an increment expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IIncrementExpression : ICompoundAssignmentExpression { /// @@ -1127,6 +1298,10 @@ public interface IIncrementExpression : ICompoundAssignmentExpression /// /// Represents a parenthesized expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IParenthesizedExpression : IOperation { /// @@ -1138,6 +1313,10 @@ public interface IParenthesizedExpression : IOperation /// /// Represents a late-bound reference to a member of a class or struct. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILateBoundMemberReferenceExpression : IOperation { /// @@ -1153,22 +1332,42 @@ public interface ILateBoundMemberReferenceExpression : IOperation /// /// Represents an argument value that has been omitted in an invocation. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IOmittedArgumentExpression : IOperation { } + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IUnboundLambdaExpression : IOperation { } + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IDefaultValueExpression : IOperation { } + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ITypeParameterObjectCreationExpression : IOperation { } + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IInvalidExpression : IOperation { } diff --git a/src/Compilers/Core/Portable/Compilation/IOperation.cs b/src/Compilers/Core/Portable/Compilation/IOperation.cs index 805f9e6911d04..d6ad22ab65ef9 100644 --- a/src/Compilers/Core/Portable/Compilation/IOperation.cs +++ b/src/Compilers/Core/Portable/Compilation/IOperation.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Semantics; namespace Microsoft.CodeAnalysis @@ -7,6 +8,11 @@ namespace Microsoft.CodeAnalysis /// /// Root type for representing the abstract semantics of C# and VB statements and expressions. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + [InternalImplementationOnly] public interface IOperation { /// diff --git a/src/Compilers/Core/Portable/Compilation/IStatement.cs b/src/Compilers/Core/Portable/Compilation/IStatement.cs index 22f72ccbc8518..41a2ce90e7326 100644 --- a/src/Compilers/Core/Portable/Compilation/IStatement.cs +++ b/src/Compilers/Core/Portable/Compilation/IStatement.cs @@ -7,6 +7,10 @@ namespace Microsoft.CodeAnalysis.Semantics /// /// Represents a block scope. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IBlockStatement : IOperation { /// @@ -22,6 +26,10 @@ public interface IBlockStatement : IOperation /// /// Represents a local variable declaration statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IVariableDeclarationStatement : IOperation { /// @@ -33,6 +41,10 @@ public interface IVariableDeclarationStatement : IOperation /// /// Represents a local variable declaration. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IVariableDeclaration : IOperation { /// @@ -48,6 +60,10 @@ public interface IVariableDeclaration : IOperation /// /// Represents a C# switch or VB Select Case statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISwitchStatement : IOperation { /// @@ -63,6 +79,10 @@ public interface ISwitchStatement : IOperation /// /// Represents a C# case or VB Case statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISwitchCase : IOperation { /// @@ -78,6 +98,10 @@ public interface ISwitchCase : IOperation /// /// Represents a clause of a C# case or a VB Case. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ICaseClause : IOperation { /// @@ -114,6 +138,10 @@ public enum CaseKind /// /// Represents case x in C# or Case x in VB. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ISingleValueCaseClause : ICaseClause { /// @@ -129,6 +157,10 @@ public interface ISingleValueCaseClause : ICaseClause /// /// Represents Case Is op x in VB. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IRelationalCaseClause : ICaseClause { /// @@ -136,7 +168,7 @@ public interface IRelationalCaseClause : ICaseClause /// IOperation Value { get; } /// - /// Relational operator used to compare the switch value with the case value. + /// Relational operator used to compare the switch value with the case value. /// BinaryOperationKind Relation { get; } } @@ -144,6 +176,10 @@ public interface IRelationalCaseClause : ICaseClause /// /// Represents Case x To y in VB. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IRangeCaseClause : ICaseClause { /// @@ -159,6 +195,10 @@ public interface IRangeCaseClause : ICaseClause /// /// Represents an if statement in C# or an If statement in VB. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IIfStatement : IOperation { /// @@ -178,6 +218,10 @@ public interface IIfStatement : IOperation /// /// Represents a C# while, for, foreach, or do statement, or a VB While, For, For Each, or Do statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILoopStatement : IOperation { /// @@ -214,6 +258,10 @@ public enum LoopKind /// /// Represents a C# while, for, or do statement, or a VB While, For, or Do statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IForWhileUntilLoopStatement : ILoopStatement { /// @@ -225,6 +273,10 @@ public interface IForWhileUntilLoopStatement : ILoopStatement /// /// Represents a C# while or do statement, or a VB While or Do statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IWhileUntilLoopStatement : IForWhileUntilLoopStatement { /// @@ -240,6 +292,10 @@ public interface IWhileUntilLoopStatement : IForWhileUntilLoopStatement /// /// Represents a C# for statement or a VB For statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IForLoopStatement : IForWhileUntilLoopStatement { /// @@ -259,6 +315,10 @@ public interface IForLoopStatement : IForWhileUntilLoopStatement /// /// Represents a C# foreach statement or a VB For Each staement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IForEachLoopStatement : ILoopStatement { /// @@ -274,6 +334,10 @@ public interface IForEachLoopStatement : ILoopStatement /// /// Represents a C# or VB label statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILabelStatement : IOperation { /// @@ -289,6 +353,10 @@ public interface ILabelStatement : IOperation /// /// Represents a C# goto, break, or continue statement, or a VB GoTo, Exit ***, or Continue *** statement /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IBranchStatement : IOperation { /// @@ -312,6 +380,10 @@ public enum BranchKind /// /// Represents a C# throw or a VB Throw statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IThrowStatement : IOperation { /// @@ -323,6 +395,10 @@ public interface IThrowStatement : IOperation /// /// Represents a C# return or a VB Return statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IReturnStatement : IOperation { /// @@ -334,6 +410,10 @@ public interface IReturnStatement : IOperation /// /// Represents a C# lock or a VB SyncLock statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ILockStatement : IOperation { /// @@ -349,6 +429,10 @@ public interface ILockStatement : IOperation /// /// Represents a C# try or a VB Try statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ITryStatement : IOperation { /// @@ -368,6 +452,10 @@ public interface ITryStatement : IOperation /// /// Represents a C# catch or VB Catch clause. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface ICatchClause : IOperation { /// @@ -391,6 +479,10 @@ public interface ICatchClause : IOperation /// /// Represents a C# using or VB Using statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IUsingStatement : IOperation { /// @@ -408,10 +500,14 @@ public interface IUsingStatement : IOperation /// IOperation Value { get; } } - + /// /// Represents a C# fixed staement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IFixedStatement : IOperation { /// @@ -427,6 +523,10 @@ public interface IFixedStatement : IOperation /// /// Represents a C# or VB statement that consists solely of an expression. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IExpressionStatement : IOperation { /// @@ -438,6 +538,10 @@ public interface IExpressionStatement : IOperation /// /// Represents a VB With statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IWithStatement : IOperation { /// @@ -453,6 +557,10 @@ public interface IWithStatement : IOperation /// /// Reprsents an empty statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IEmptyStatement : IOperation { } @@ -460,6 +568,10 @@ public interface IEmptyStatement : IOperation /// /// Represents a VB Stop statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IStopStatement : IOperation { } @@ -467,6 +579,10 @@ public interface IStopStatement : IOperation /// /// Represents a VB End statemnt. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IEndStatement : IOperation { } @@ -474,6 +590,10 @@ public interface IEndStatement : IOperation /// /// Represents a syntactically or semantically invalid C# or VB statement. /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// public interface IInvalidStatement : IOperation { } diff --git a/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbol.cs b/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbol.cs index 6245d1c136fed..d7b2bd79bf137 100644 --- a/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/ISourceAssemblySymbol.cs @@ -16,7 +16,6 @@ namespace Microsoft.CodeAnalysis /// This interface is reserved for implementation by its associated APIs. We reserve the right to /// change it in the future. /// - [InternalImplementationOnly] public interface ISourceAssemblySymbol : IAssemblySymbol { Compilation Compilation { get; } diff --git a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index 8d63a6627b244..d1c64da2f2b72 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -51,7 +51,7 @@ static CompilerServerUnitTests() // VBCSCompiler is used as a DLL in these tests, need to hook the resolve to the installed location. AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve; - basePath = GetMSBuildDirectory(); + basePath = TestHelpers.GetMSBuildDirectory(); if (basePath == null) { return; @@ -74,23 +74,6 @@ private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs e) return null; } - private static string GetMSBuildDirectory() - { - using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0", false)) - { - if (key != null) - { - var toolsPath = key.GetValue("MSBuildToolsPath"); - if (toolsPath != null) - { - return toolsPath.ToString(); - } - } - } - - return null; - } - private static readonly KeyValuePair[] s_helloWorldSrcCs = { new KeyValuePair("hello.cs", diff --git a/src/Dependencies/VisualStudio/project.json b/src/Dependencies/VisualStudio/project.json index efefe50e36041..b2d2398bde01f 100644 --- a/src/Dependencies/VisualStudio/project.json +++ b/src/Dependencies/VisualStudio/project.json @@ -2,15 +2,15 @@ "supports": { }, "dependencies": { "Microsoft.VisualStudio.Designer.Interfaces": "1.1.4322", - "Microsoft.VisualStudio.Editor": "14.1.24720", - "Microsoft.VisualStudio.ImageCatalog": "14.1.24720", + "Microsoft.VisualStudio.Editor": "14.2.25123", + "Microsoft.VisualStudio.ImageCatalog": "14.2.25123", "Microsoft.VisualStudio.OLE.Interop": "7.10.6070", - "Microsoft.VisualStudio.Shell.Design": "14.1.24720", - "Microsoft.VisualStudio.Shell.14.0": "14.1.24720", + "Microsoft.VisualStudio.Shell.Design": "14.2.25123", + "Microsoft.VisualStudio.Shell.14.0": "14.2.25123", "Microsoft.VisualStudio.Shell.Interop.10.0": "10.0.30319", "Microsoft.VisualStudio.Shell.Interop.11.0": "11.0.61030", "Microsoft.VisualStudio.Shell.Interop.12.1.DesignTime": "12.1.30328", - "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime": "14.1.24720", + "Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime": "14.2.25123", "Microsoft.VisualStudio.TextManager.Interop.10.0": "10.0.30319", "Microsoft.VisualStudio.TextManager.Interop.12.0": "12.0.30110", "Microsoft.VisualStudio.TextManager.Interop.12.1.DesignTime": "12.1.30328", diff --git a/src/Dependencies/VisualStudioEditor/project.json b/src/Dependencies/VisualStudioEditor/project.json index 3935c317cb7d4..7f30373444fcb 100644 --- a/src/Dependencies/VisualStudioEditor/project.json +++ b/src/Dependencies/VisualStudioEditor/project.json @@ -1,11 +1,11 @@ { "supports": { }, "dependencies": { - "Microsoft.VisualStudio.Imaging": "14.1.24720", - "Microsoft.VisualStudio.ImageCatalog": "14.1.24720", - "Microsoft.VisualStudio.Language.Intellisense": "14.1.24720", - "Microsoft.VisualStudio.Language.StandardClassification": "14.1.24720", - "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "14.1.24720" + "Microsoft.VisualStudio.Imaging": "14.2.25123", + "Microsoft.VisualStudio.ImageCatalog": "14.2.25123", + "Microsoft.VisualStudio.Language.Intellisense": "14.2.25123", + "Microsoft.VisualStudio.Language.StandardClassification": "14.2.25123", + "Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime": "14.2.25123" }, "frameworks": { ".NETFramework,Version=v4.6": { } diff --git a/src/Dependencies/VisualStudioText/project.json b/src/Dependencies/VisualStudioText/project.json index 445779fb9eaea..5827594f42b6c 100644 --- a/src/Dependencies/VisualStudioText/project.json +++ b/src/Dependencies/VisualStudioText/project.json @@ -1,12 +1,13 @@ { "supports": { }, "dependencies": { - "Microsoft.VisualStudio.Text.Data": "14.1.24720", - "Microsoft.VisualStudio.Text.Logic": "14.1.24720", - "Microsoft.VisualStudio.Text.UI": "14.1.24720", - "Microsoft.VisualStudio.Text.UI.Wpf": "14.1.24720" + "Microsoft.VisualStudio.Text.Data": "14.2.25123", + "Microsoft.VisualStudio.Text.Logic": "14.2.25123", + "Microsoft.VisualStudio.Text.UI": "14.2.25123", + "Microsoft.VisualStudio.Text.UI.Wpf": "14.2.25123", + "RoslynDependencies.Microsoft.VisualStudio.Text.Internal": "14.2.25123" }, "frameworks": { ".NETFramework,Version=v4.6": { } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 99ca30785b2d4..1914820690883 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -105,10 +105,6 @@ - - - - @@ -518,4 +514,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj index 33ea43692fb94..d2eb1d60227a3 100644 --- a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj +++ b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj @@ -109,10 +109,6 @@ - - - - @@ -265,4 +261,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index ad4e43550e031..6293269c70a1c 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -59,11 +59,6 @@ true - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Language.CallHierarchy.dll - - - @@ -80,6 +75,7 @@ + @@ -109,6 +105,7 @@ + @@ -227,6 +224,8 @@ + + @@ -258,6 +257,7 @@ + @@ -275,9 +275,10 @@ - - - + + + + @@ -484,7 +485,6 @@ - @@ -804,4 +804,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Core/Extensibility/Composition/VisualStudioVersionMetadata.cs b/src/EditorFeatures/Core/Extensibility/Composition/VisualStudioVersionMetadata.cs new file mode 100644 index 0000000000000..6fc6fc83ceb6b --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/Composition/VisualStudioVersionMetadata.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Extensibility.Composition +{ + internal sealed class VisualStudioVersionMetadata + { + public VisualStudioVersion Version { get; } + + public VisualStudioVersionMetadata(IDictionary data) + { + Version = (VisualStudioVersion)data.GetValueOrDefault("Version"); + } + } +} diff --git a/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs b/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs new file mode 100644 index 0000000000000..e17b76e9733f4 --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor +{ + /// + /// Note: This need to be in ascending order, since we compare values in + /// . + /// + internal enum VisualStudioVersion + { + /// VS Version 14, aka 'VS 2015' + Dev14 = 14, + /// VS Version 15, aka 'VS "15"' + Dev15 = 15, + } + + [MetadataAttribute] + [AttributeUsage(AttributeTargets.Class)] + internal sealed class ExportVersionSpecificAttribute : ExportAttribute + { + public VisualStudioVersion Version { get; } + public ExportVersionSpecificAttribute(Type contractType, VisualStudioVersion version) + : base(contractType) + { + this.Version = version; + } + } +} diff --git a/src/EditorFeatures/Core/Extensibility/VersionSelector.cs b/src/EditorFeatures/Core/Extensibility/VersionSelector.cs new file mode 100644 index 0000000000000..0dc2c7000004d --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/VersionSelector.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Extensibility.Composition; + +namespace Microsoft.CodeAnalysis.Editor +{ + internal sealed class VersionSelector + { + public static T SelectHighest(IEnumerable> items) + { + return items.OrderByDescending(i => i.Metadata.Version).First().Value; + } + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs index 010926eff0b20..38156510624e1 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Collections.Generic; using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.Editor.Extensibility.Composition; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; @@ -17,18 +20,24 @@ internal sealed class CompletionPresenter : ForegroundThreadAffinitizedObject, I { private readonly ICompletionBroker _completionBroker; private readonly IGlyphService _glyphService; + private readonly ICompletionSetFactory _completionSetFactory; [ImportingConstructor] - public CompletionPresenter(ICompletionBroker completionBroker, IGlyphService glyphService) + public CompletionPresenter( + ICompletionBroker completionBroker, + IGlyphService glyphService, + [ImportMany] IEnumerable> completionSetFactories) { _completionBroker = completionBroker; _glyphService = glyphService; + _completionSetFactory = VersionSelector.SelectHighest(completionSetFactories); } ICompletionPresenterSession IIntelliSensePresenter.CreateSession(ITextView textView, ITextBuffer subjectBuffer, ICompletionSession session) { AssertIsForeground(); - return new CompletionPresenterSession(_completionBroker, _glyphService, textView, subjectBuffer); + return new CompletionPresenterSession( + _completionSetFactory, _completionBroker, _glyphService, textView, subjectBuffer); } ICompletionSource ICompletionSourceProvider.TryCreateCompletionSource(ITextBuffer textBuffer) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs index 8bad59fb68968..e6944660608ab 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs @@ -28,7 +28,7 @@ internal sealed class CompletionPresenterSession : ForegroundThreadAffinitizedOb public event EventHandler ItemSelected; public event EventHandler FilterStateChanged; - private readonly CompletionSet3 _completionSet; + private readonly ICompletionSet _completionSet; private ICompletionSession _editorSessionOpt; private bool _ignoreSelectionStatusChangedEvent; @@ -44,6 +44,7 @@ public ITextBuffer SubjectBuffer } public CompletionPresenterSession( + ICompletionSetFactory completionSetFactory, ICompletionBroker completionBroker, IGlyphService glyphService, ITextView textView, @@ -54,7 +55,7 @@ public ITextBuffer SubjectBuffer _textView = textView; _subjectBuffer = subjectBuffer; - _completionSet = new CompletionSet3(this, textView, subjectBuffer); + _completionSet = completionSetFactory.CreateCompletionSet(this, textView, subjectBuffer); _completionSet.SelectionStatusChanged += OnCompletionSetSelectionStatusChanged; } @@ -156,15 +157,14 @@ private void OnCompletionSetSelectionStatusChanged(object sender, ValueChangedEv internal void AugmentCompletionSession(IList completionSets) { - Contract.ThrowIfTrue(completionSets.Contains(_completionSet)); - completionSets.Add(_completionSet); + Contract.ThrowIfTrue(completionSets.Contains(_completionSet.CompletionSet)); + completionSets.Add(_completionSet.CompletionSet); } - internal void OnIntelliSenseFiltersChanged(IReadOnlyList filters) + internal void OnIntelliSenseFiltersChanged(ImmutableDictionary filterStates) { this.FilterStateChanged?.Invoke(this, - new CompletionItemFilterStateChangedEventArgs( - filters.ToImmutableDictionary(f => f.CompletionItemFilter, f => f.IsChecked))); + new CompletionItemFilterStateChangedEventArgs(filterStates)); } public void Dismiss() diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs new file mode 100644 index 0000000000000..3445ff9f6c750 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using VSCompletion = Microsoft.VisualStudio.Language.Intellisense.Completion; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation +{ + internal interface ICompletionSet + { + event EventHandler> SelectionStatusChanged; + void SetTrackingSpan(ITrackingSpan trackingSpan); + + CompletionSet CompletionSet { get; } + + void SetCompletionItems( + IList completionItems, + PresentationItem selectedItem, + PresentationItem presetBuilder, + bool suggestionMode, + bool isSoftSelected, + ImmutableArray completionItemFilters, + IReadOnlyDictionary completionItemToFilterText); + PresentationItem GetPresentationItem(VSCompletion completion); + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs new file mode 100644 index 0000000000000..f2daef0ee25cf --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs @@ -0,0 +1,18 @@ +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation +{ + /// + /// We have two implementations of ICompletionSet that are specific to different VS versions + /// because the newer one lights up new functionality from the platform. This let's the + /// presenter create the right one. + /// + internal interface ICompletionSetFactory + { + ICompletionSet CreateCompletionSet( + CompletionPresenterSession completionPresenterSession, + ITextView textView, + ITextBuffer subjectBuffer); + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionSet3.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs similarity index 91% rename from src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionSet3.cs rename to src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs index 4481b873ade61..3d9fa05b0640a 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionSet3.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs @@ -15,10 +15,10 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation { -#if NEWCOMPLETION - internal sealed class CompletionSet3 : CompletionSet2 +#if DEV15 + internal sealed class FilteredRoslynCompletionSet : CompletionSet2, ICompletionSet #else - internal sealed class CompletionSet3 : CompletionSet + internal sealed class RoslynCompletionSet : CompletionSet, ICompletionSet #endif { private readonly ForegroundThreadAffinitizedObject _foregroundObject = new ForegroundThreadAffinitizedObject(); @@ -27,11 +27,17 @@ internal sealed class CompletionSet3 : CompletionSet private readonly CompletionPresenterSession _completionPresenterSession; private Dictionary _presentationItemMap; + private IReadOnlyDictionary _completionItemToFilterText; + +#if DEV15 private CompletionHelper _completionHelper; private IReadOnlyList _filters; - private IReadOnlyDictionary _completionItemToFilterText; + public override IReadOnlyList Filters => _filters; - public CompletionSet3( + public FilteredRoslynCompletionSet( +#else + public RoslynCompletionSet( +#endif CompletionPresenterSession completionPresenterSession, ITextView textView, ITextBuffer subjectBuffer) @@ -43,19 +49,15 @@ internal sealed class CompletionSet3 : CompletionSet this.DisplayName = "All"; } -#if NEWCOMPLETION - public override IReadOnlyList Filters => _filters; -#endif - - internal void SetTrackingSpan(ITrackingSpan trackingSpan) + void ICompletionSet.SetTrackingSpan(ITrackingSpan trackingSpan) { this.ApplicableTo = trackingSpan; } - internal void SetCompletionItems( + void ICompletionSet.SetCompletionItems( IList completionItems, PresentationItem selectedItem, - PresentationItem suggestionModeItem, + PresentationItem presetBuilder, bool suggestionMode, bool isSoftSelected, ImmutableArray completionItemFilters, @@ -74,12 +76,14 @@ internal void SetTrackingSpan(ITrackingSpan trackingSpan) this.WritableCompletionBuilders.BeginBulkOperation(); this.WritableCompletionBuilders.Clear(); +#if DEV15 // If more than one filter was provided, then present it to the user. if (_filters == null && completionItemFilters.Length > 1) { _filters = completionItemFilters.Select(f => new IntellisenseFilter2(this, f, GetLanguage())) .ToArray(); } +#endif var applicableToText = this.ApplicableTo.GetText(this.ApplicableTo.TextBuffer.CurrentSnapshot); @@ -90,8 +94,8 @@ internal void SetTrackingSpan(ITrackingSpan trackingSpan) selectedItem.CompletionService, isSuggestionModeItem: true); - var showBuilder = suggestionMode || suggestionModeItem != null; - var bestSuggestionModeItem = applicableToText.Length > 0 ? filteredSuggestionModeItem : suggestionModeItem ?? filteredSuggestionModeItem; + var showBuilder = suggestionMode || presetBuilder != null; + var bestSuggestionModeItem = applicableToText.Length > 0 ? filteredSuggestionModeItem : presetBuilder ?? filteredSuggestionModeItem; if (showBuilder && bestSuggestionModeItem != null) { @@ -148,7 +152,7 @@ private VSCompletion GetVSCompletion(PresentationItem item) return value; } - internal PresentationItem GetPresentationItem(VSCompletion completion) + PresentationItem ICompletionSet.GetPresentationItem(VSCompletion completion) { // Linear search is ok since this is only called by the user manually selecting // an item. Creating a reverse mapping uses too much memory and affects GCs. @@ -191,6 +195,7 @@ private string GetLanguage() return ""; } +#if DEV15 private CompletionHelper GetCompletionHelper() { _foregroundObject.AssertIsForeground(); @@ -206,11 +211,7 @@ private CompletionHelper GetCompletionHelper() return _completionHelper; } -#if NEWCOMPLETION public override IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) -#else - public IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) -#endif { if (_completionItemToFilterText != null) { @@ -239,7 +240,11 @@ public IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) internal void OnIntelliSenseFiltersChanged() { - this._completionPresenterSession.OnIntelliSenseFiltersChanged(_filters); + this._completionPresenterSession.OnIntelliSenseFiltersChanged( + _filters.ToImmutableDictionary(f => f.CompletionItemFilter, f => f.IsChecked)); } +#endif + + CompletionSet ICompletionSet.CompletionSet => this; } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs new file mode 100644 index 0000000000000..db6b7562db0d3 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation +{ + [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.Dev14)] + internal sealed class VisualStudio14CompletionSetFactory : ICompletionSetFactory + { + public ICompletionSet CreateCompletionSet( + CompletionPresenterSession completionPresenterSession, + ITextView textView, + ITextBuffer subjectBuffer) + { + return new RoslynCompletionSet( + completionPresenterSession, textView, subjectBuffer); + } + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IIntellisenseFilter.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IIntellisenseFilter.cs deleted file mode 100644 index 5bf81f1a5aea7..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IIntellisenseFilter.cs +++ /dev/null @@ -1,46 +0,0 @@ -#if !NEWCOMPLETION -// Copyright (c) Microsoft Corporation -// All rights reserved -// REMOVE ONCE WE ACTUALLY REFERENCE THE REAL EDITOR DLLS. -using Microsoft.VisualStudio.Imaging.Interop; - -namespace Microsoft.VisualStudio.Language.Intellisense -{ - /// - /// Defines a filter used to add a row of filter buttons to the bottom - /// - internal interface IIntellisenseFilter - { - /// - /// The icon shown on the filter's button. - /// - ImageMoniker Moniker { get; } - /// - /// The tooltip shown when the mouse hovers over the button. - /// - string ToolTip { get; } - /// - /// The key used to toggle the filter's state. - /// - string AccessKey { get; } - /// - /// String used to represent the button for automation. - /// - string AutomationText { get; } - /// - /// Has the user turned the filter on? - /// - /// - /// The setter will be called when the user toggles the corresponding filter button. - /// - bool IsChecked { get; set; } - /// - /// Is the filter enabled? - /// - /// - /// Disabled filters are shown but are grayed out. - /// - bool IsEnabled { get; set; } - } -} -#endif \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IntellisenseFilter.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IntellisenseFilter.cs deleted file mode 100644 index 4ae35ac1d7364..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IntellisenseFilter.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if !NEWCOMPLETION -// Copyright (c) Microsoft Corporation -// All rights reserved -// REMOVE ONCE WE ACTUALLY REFERENCE THE REAL EDITOR DLLS. -using System; -using Microsoft.VisualStudio.Imaging.Interop; - -namespace Microsoft.VisualStudio.Language.Intellisense -{ - internal class IntellisenseFilter : IIntellisenseFilter - { - /// - /// Create an instance of an IntellisenseFilter with the specified attributes. - /// - public IntellisenseFilter(ImageMoniker moniker, string toolTip, string accessKey, string automationText, bool initialIsChecked = false, bool initialIsEnabled = true) - { - if (string.IsNullOrEmpty(accessKey)) - { - throw new ArgumentException("Must not be null or empty", nameof(accessKey)); - } - this.Moniker = moniker; - this.ToolTip = toolTip; - this.AccessKey = accessKey; - this.AutomationText = automationText; - this.IsChecked = initialIsChecked; - this.IsEnabled = initialIsEnabled; - } - /// - /// The icon shown on the filter's button. - /// - public ImageMoniker Moniker { get; } - /// - /// The tooltip shown when the mouse hovers over the button. - /// - public string ToolTip { get; } - /// - /// The key used to toggle the filter's state. - /// - public string AccessKey { get; } - /// - /// String used to represent the button for automation. - /// - public string AutomationText { get; } - /// - /// Has the user turned the filter on? - /// - /// - /// The setter will be called when the user toggles the corresponding filter button. - /// - public virtual bool IsChecked { get; set; } - /// - /// Is the filter enabled? - /// - /// - /// Disabled filters are shown but are grayed out. - /// Intellisense will never call the setter but the owner may and the Intellisense popup will respect the changes. - /// - public bool IsEnabled { get; set; } - } -} -#endif \ No newline at end of file diff --git a/src/EditorFeatures/Core/project.json b/src/EditorFeatures/Core/project.json index 718f042f58ddd..398f006138a8e 100644 --- a/src/EditorFeatures/Core/project.json +++ b/src/EditorFeatures/Core/project.json @@ -1,5 +1,7 @@ { "dependencies": { + "RoslynDependencies.Microsoft.VisualStudio.Language.CallHierarchy": "14.0.23107", + "RoslynDependencies.Microsoft.VisualStudio.Language.NavigateTo.Interfaces": "14.0.23107" }, "frameworks": { "net46": {} diff --git a/src/EditorFeatures/Next/EditorFeatures.Next.csproj b/src/EditorFeatures/Next/EditorFeatures.Next.csproj new file mode 100644 index 0000000000000..eacb774c7ea8b --- /dev/null +++ b/src/EditorFeatures/Next/EditorFeatures.Next.csproj @@ -0,0 +1,85 @@ + + + + CSharp + + + + + + Debug + AnyCPU + {366BBCDC-B05F-4677-9B5B-78BA816A1484} + Library + Microsoft.CodeAnalysis.Editor + Microsoft.CodeAnalysis.EditorFeatures.Next + v4.6 + false + DEV15 + + + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} + CodeAnalysis + + + {DCDA908D-EF5E-494B-ADDC-C26F5FD610CA} + Immutable + + + {ef986d9b-8cfc-4ecb-9729-e260a1f84aff} + VisualStudioEditor + + + {2e87fa96-50bb-4607-8676-46521599f998} + Workspaces.Desktop + + + {5F8D2414-064A-4B3A-9B42-8E2A04246BE5} + Workspaces + + + {EDC68A0E-C68D-4A74-91B7-BF38EC909888} + Features + + + {3cdeeab7-2256-418a-beb2-620b5cb16302} + EditorFeatures + + + {18F5FBB8-7570-4412-8CC7-0A86FF13B7BA} + TextEditorFeatures + + + {01E9BD68-0339-4A13-B42F-A3CA84D164F3} + InteractiveWindow + InteractiveWindow + + + + true + + + true + + + + + + + + + + + + + + IntelliSense\Completion\Presentation\RoslynCompletionSet.cs + + + + + + + + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs similarity index 87% rename from src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs rename to src/EditorFeatures/Next/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs index 2a10c22501b70..5187cdb8e5a95 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs @@ -5,11 +5,11 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.P { internal class IntellisenseFilter2 : IntellisenseFilter { - private readonly CompletionSet3 _completionSet; + private readonly FilteredRoslynCompletionSet _completionSet; public readonly CompletionItemFilter CompletionItemFilter; public IntellisenseFilter2( - CompletionSet3 completionSet, CompletionItemFilter filter, string language) + FilteredRoslynCompletionSet completionSet, CompletionItemFilter filter, string language) : base(ImageMonikers.GetImageMoniker(filter.Tags, language), GetToolTip(filter), filter.AccessKey.ToString(), automationText: filter.Tags[0]) { diff --git a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs new file mode 100644 index 0000000000000..cc21608cbebe6 --- /dev/null +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs @@ -0,0 +1,18 @@ +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation +{ + [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.Dev15)] + internal sealed class VisualStudio15CompletionSetFactory : ICompletionSetFactory + { + public ICompletionSet CreateCompletionSet( + CompletionPresenterSession completionPresenterSession, + ITextView textView, + ITextBuffer subjectBuffer) + { + return new FilteredRoslynCompletionSet( + completionPresenterSession, textView, subjectBuffer); + } + } +} diff --git a/src/EditorFeatures/Next/PublicAPI.Shipped.txt b/src/EditorFeatures/Next/PublicAPI.Shipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/EditorFeatures/Next/PublicAPI.Unshipped.txt b/src/EditorFeatures/Next/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/EditorFeatures/Next/project.json b/src/EditorFeatures/Next/project.json new file mode 100644 index 0000000000000..3d2e6fbd053de --- /dev/null +++ b/src/EditorFeatures/Next/project.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "Microsoft.VisualStudio.Language.Intellisense": "15.0.25123-Dev15Preview", + }, + "frameworks": { + "net46": {} + } +} diff --git a/src/EditorFeatures/Test/EditorServicesTest.csproj b/src/EditorFeatures/Test/EditorServicesTest.csproj index 5c6c2a60deb32..beab9602190e0 100644 --- a/src/EditorFeatures/Test/EditorServicesTest.csproj +++ b/src/EditorFeatures/Test/EditorServicesTest.csproj @@ -18,11 +18,6 @@ true true - - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Platform.VSEditor.Interop.dll - - {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} @@ -144,10 +139,6 @@ - - - - @@ -350,4 +341,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Test/project.json b/src/EditorFeatures/Test/project.json index 776ced8e60381..335400b75762b 100644 --- a/src/EditorFeatures/Test/project.json +++ b/src/EditorFeatures/Test/project.json @@ -2,6 +2,7 @@ "dependencies": { "BasicUndo": "0.9.3", "Microsoft.VisualStudio.Composition": "14.0.50715-pre", + "RoslynDependencies.Microsoft.VisualStudio.Platform.VSEditor": "14.2.25123" }, "frameworks": { "net46": {} diff --git a/src/EditorFeatures/Test2/EditorServicesTest2.vbproj b/src/EditorFeatures/Test2/EditorServicesTest2.vbproj index b9ce4afed98db..0c2a70a29207b 100644 --- a/src/EditorFeatures/Test2/EditorServicesTest2.vbproj +++ b/src/EditorFeatures/Test2/EditorServicesTest2.vbproj @@ -116,9 +116,6 @@ Roslyn.Services.Editor.UnitTests2.xml - - - @@ -302,4 +299,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Text/TextEditorFeatures.csproj b/src/EditorFeatures/Text/TextEditorFeatures.csproj index e914166049156..7ca636907970e 100644 --- a/src/EditorFeatures/Text/TextEditorFeatures.csproj +++ b/src/EditorFeatures/Text/TextEditorFeatures.csproj @@ -32,9 +32,6 @@ - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Text.Internal.dll - @@ -43,6 +40,7 @@ + @@ -94,4 +92,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index 4888c0cd830d5..71c32767c59af 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -91,9 +91,6 @@ true - - - @@ -612,4 +609,4 @@ - \ No newline at end of file + diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/DebuggerVisualizerAttributeTests.cs b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/DebuggerVisualizerAttributeTests.cs index 061512d132b04..e7cbf67c7d610 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/DebuggerVisualizerAttributeTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/DebuggerVisualizerAttributeTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.CodeAnalysis.ExpressionEvaluator; using Microsoft.VisualStudio.Debugger.Clr; using Microsoft.VisualStudio.Debugger.Evaluation; @@ -44,7 +45,8 @@ class Q var typeQ = assembly.GetType("Q"); string defaultDebuggeeSideVisualizerTypeName = "Microsoft.VisualStudio.DebuggerVisualizers.VisualizerObjectSource"; - string defaultDebuggeeSideVisualizerAssemblyName = "Microsoft.VisualStudio.DebuggerVisualizers, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; + string defaultDebuggeeSideVisualizerAssemblyName = $"Microsoft.VisualStudio.DebuggerVisualizers, Version={vsVersion}.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; DkmCustomUIVisualizerInfo[] customUIVisualizerInfo = { diff --git a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrType.cs b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrType.cs index 7d63682bd9ce8..4602ff8ac61c7 100644 --- a/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrType.cs +++ b/src/ExpressionEvaluator/Core/Test/ResultProvider/Debugger/Engine/DkmClrType.cs @@ -299,7 +299,8 @@ private static DkmClrDebuggerVisualizerAttribute[] GetDebuggerVisualizerAttribut else { debuggeeSideVisualizerTypeName = "Microsoft.VisualStudio.DebuggerVisualizers.VisualizerObjectSource"; - debuggeeSideVisualizerAssemblyName = "Microsoft.VisualStudio.DebuggerVisualizers, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + var vsVersion = System.Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; + debuggeeSideVisualizerAssemblyName = $"Microsoft.VisualStudio.DebuggerVisualizers, Version={vsVersion}.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; } string visualizerDescription = uiSideVisualizerTypeName; diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 1094fc0715372..a928bb66783f2 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -36,6 +36,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs b/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs index e0d9e9051078d..eccbabf03deb7 100644 --- a/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs +++ b/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs @@ -9,6 +9,9 @@ using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.InteractiveWindow; using Microsoft.VisualStudio.InteractiveWindow.Commands; +using System.Collections.Generic; +using System; +using Microsoft.CodeAnalysis.Editor.Extensibility.Composition; namespace Microsoft.CodeAnalysis.Editor.Implementation.Completion.Presentation { @@ -19,18 +22,24 @@ internal partial class CompletionPresenter : ForegroundThreadAffinitizedObject, { private readonly ICompletionBroker _completionBroker; private readonly IGlyphService _glyphService; + private readonly ICompletionSetFactory _completionSetFactory; [ImportingConstructor] - public CompletionPresenter(ICompletionBroker completionBroker, IGlyphService glyphService) + public CompletionPresenter( + ICompletionBroker completionBroker, + IGlyphService glyphService, + [ImportMany] IEnumerable> completionSetFactories) { _completionBroker = completionBroker; _glyphService = glyphService; + _completionSetFactory = VersionSelector.SelectHighest(completionSetFactories); } ICompletionPresenterSession IIntelliSensePresenter.CreateSession(ITextView textView, ITextBuffer subjectBuffer, ICompletionSession sessionOpt) { AssertIsForeground(); - return new CompletionPresenterSession(_completionBroker, _glyphService, textView, subjectBuffer); + return new CompletionPresenterSession( + _completionSetFactory, _completionBroker, _glyphService, textView, subjectBuffer); } ICompletionSource ICompletionSourceProvider.TryCreateCompletionSource(ITextBuffer textBuffer) diff --git a/src/InteractiveWindow/Editor/InteractiveWindow.csproj b/src/InteractiveWindow/Editor/InteractiveWindow.csproj index 465892b7ccf20..92aebded3ae88 100644 --- a/src/InteractiveWindow/Editor/InteractiveWindow.csproj +++ b/src/InteractiveWindow/Editor/InteractiveWindow.csproj @@ -26,10 +26,6 @@ - - False - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Text.Internal.dll - @@ -136,4 +132,4 @@ - \ No newline at end of file + diff --git a/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj b/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj index ba9821948ddfa..48b1bf2cc6451 100644 --- a/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj +++ b/src/InteractiveWindow/EditorTest/InteractiveWindowTest.csproj @@ -49,13 +49,6 @@ - - - $(DevEnvDir)\CommonExtensions\Microsoft\Editor\Microsoft.VisualStudio.Platform.VSEditor.dll - - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Platform.VSEditor.Interop.dll - @@ -90,4 +83,4 @@ - \ No newline at end of file + diff --git a/src/InteractiveWindow/EditorTest/project.json b/src/InteractiveWindow/EditorTest/project.json index e53cf7d8d3964..be64b90fb3ac3 100644 --- a/src/InteractiveWindow/EditorTest/project.json +++ b/src/InteractiveWindow/EditorTest/project.json @@ -1,6 +1,7 @@ { "dependencies": { "BasicUndo": "0.9.3", + "RoslynDependencies.Microsoft.VisualStudio.Platform.VSEditor": "14.2.25123" }, "frameworks": { "net46": {} @@ -8,4 +9,4 @@ "runtimes": { "win7-x86": {} } -} \ No newline at end of file +} diff --git a/src/Test/Perf/bootstrap.bat b/src/Test/Perf/bootstrap.bat index 374ec5eeede51..936ec284ec277 100644 --- a/src/Test/Perf/bootstrap.bat +++ b/src/Test/Perf/bootstrap.bat @@ -2,8 +2,8 @@ call "%~dp0..\..\..\Restore.cmd" -set MSBuild=%ProgramFiles%\MSBuild\14.0\bin\msbuild.exe -if not exist "%MSBuild%" set MSBuild=%ProgramFiles(x86)%\MSBuild\14.0\bin\msbuild.exe +set MSBuild=%ProgramFiles%\MSBuild\%VisualStudioVersion%\bin\msbuild.exe +if not exist "%MSBuild%" set MSBuild=%ProgramFiles(x86)%\MSBuild\%VisualStudioVersion%\bin\msbuild.exe "%MSBuild%" "%~dp0..\..\..\src\Interactive\csi\csi.csproj" /p:Configuration=Release /p:OutDir="%~dp0infra\bin\\" if "%USERDNSDOMAIN%" == "REDMOND.CORP.MICROSOFT.COM" ( @@ -12,4 +12,4 @@ if "%USERDNSDOMAIN%" == "REDMOND.CORP.MICROSOFT.COM" ( robocopy \\mlangfs1\public\basoundr\vibenchcsv2json %SYSTEMDRIVE%\CPC /s ) else ( echo "Machine not in Microsoft Corp Net Domain. Hence not downloading internal tools" -) \ No newline at end of file +) diff --git a/src/Test/Perf/infra/install.csx b/src/Test/Perf/infra/install.csx index 395fc3b65768a..b56daa17ba695 100644 --- a/src/Test/Perf/infra/install.csx +++ b/src/Test/Perf/infra/install.csx @@ -12,6 +12,7 @@ using System.Diagnostics; using System.IO; using Roslyn.Test.Performance.Utilities; using static Roslyn.Test.Performance.Utilities.TestUtilities; +using static System.FormattableString; TestUtilities.InitUtilitiesFromCsx(); @@ -35,7 +36,8 @@ foreach (var processName in new[] { "devenv", "msbuild", "VBCSCompiler"}) var logger = new ConsoleAndFileLogger(); logger.Log($"\n{message} Roslyn binaries to VS folder."); -var devenvFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Microsoft Visual Studio 14.0\Common7\IDE"); +var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; +var devenvFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), Invariant($@"Microsoft Visual Studio {vsVersion}\Common7\IDE")); var destinationFolder = Path.Combine(devenvFolder, "PrivateAssemblies"); var filesToNGen = new List(); foreach (var file in IDEFiles) @@ -60,8 +62,8 @@ ShellOutVital(devenv, "/updateconfiguration", IsVerbose(), logger); ShellOutVital(devenv, $"/resetsettingsfull {Path.Combine(sourceFolder, "Default.vssettings")} /command \"File.Exit\"", IsVerbose(), logger); logger.Log($"\n{message} compilers in MSBuild folders."); -destinationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"MSBuild\14.0\Bin"); -var destinationFolder64 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"MSBuild\14.0\Bin\amd64"); +destinationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), Invariant($@"MSBuild\{vsVersion}\Bin")); +var destinationFolder64 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), Invariant($@"MSBuild\14.0\{vsVersion}\amd64")); filesToNGen = new List(); foreach (var file in MSBuildFiles) { diff --git a/src/Test/Utilities/Desktop/TestHelpers.cs b/src/Test/Utilities/Desktop/TestHelpers.cs index 0d72db714f760..df087788134c7 100644 --- a/src/Test/Utilities/Desktop/TestHelpers.cs +++ b/src/Test/Utilities/Desktop/TestHelpers.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.Win32; namespace Roslyn.Test.Utilities { @@ -145,5 +146,24 @@ public static string AsXmlCommentText(string text) Debug.Assert(!result.Contains("--")); return result; } + + public static string GetMSBuildDirectory() + { + var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; + using (var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}", false)) + { + if (key != null) + { + var toolsPath = key.GetValue("MSBuildToolsPath"); + if (toolsPath != null) + { + return toolsPath.ToString(); + } + } + } + + return null; + } + } } diff --git a/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj index a28a9bd238516..2ee17a0ee6bf5 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj +++ b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj @@ -93,7 +93,6 @@ false - @@ -270,4 +269,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs index d55e278dcdf14..a64177cc27c10 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs @@ -33,6 +33,12 @@ public int AutoComment set { SetBooleanOption(FeatureOnOffOptions.AutoXmlDocCommentGeneration, value); } } + public int AutoInsertAsteriskForNewLinesOfBlockComments + { + get { return GetBooleanOption(FeatureOnOffOptions.AutoInsertBlockCommentStartString); } + set { SetBooleanOption(FeatureOnOffOptions.AutoInsertBlockCommentStartString, value); } + } + public int BringUpOnIdentifier { get { return GetBooleanOption(CompletionOptions.TriggerOnTypingLetters); } diff --git a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs index c31fbb5b62dc4..4a78e6517145c 100644 --- a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs +++ b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs @@ -161,6 +161,7 @@ private bool SupportsOnOffOption(IOption option) option == FeatureOnOffOptions.KeywordHighlighting || option == FeatureOnOffOptions.FormatOnPaste || option == FeatureOnOffOptions.AutoXmlDocCommentGeneration || + option == FeatureOnOffOptions.AutoInsertBlockCommentStartString || option == FeatureOnOffOptions.RefactoringVerification || option == FeatureOnOffOptions.RenameTracking || option == FeatureOnOffOptions.RenameTrackingPreview; diff --git a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj index acc45e5ece87f..68a4432990438 100644 --- a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj +++ b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj @@ -15,11 +15,6 @@ v4.6 - - - $(DevEnvDir)\CommonExtensions\Microsoft\Editor\Microsoft.VisualStudio.Platform.VSEditor.dll - - {92412d1a-0f23-45b5-b196-58839c524917} @@ -131,9 +126,6 @@ false - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Text.Internal.dll - @@ -205,4 +197,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/SuppressionStateColumnFilterDefinition.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/SuppressionStateColumnFilterDefinition.cs deleted file mode 100644 index 3ee9b9afaa3cc..0000000000000 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/SuppressionStateColumnFilterDefinition.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.ComponentModel.Composition; -using Microsoft.VisualStudio.Utilities; -using Microsoft.Internal.VisualStudio.Shell.TableControl; -using System; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource -{ - // we are disabling this due to this issue. https://github.com/dotnet/roslyn/pull/9201 - - // - // Supporession column definition filter - // - // - // TODO: Move this column down to the shell as it is shared by multiple issue sources (Roslyn and FxCop). - // - // [Export(typeof(EntryFilterDefinition))] - // [Name(SuppressionStateColumnDefinition.ColumnName)] - // internal class SuppressionStateColumnFilterDefinition : EntryFilterDefinition - // { - // public override bool HasAttribute(string key) => string.Equals(NonActionable, key, StringComparison.OrdinalIgnoreCase); - // } -} - diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 8ab649beed204..71f32589f2442 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -107,7 +107,6 @@ - @@ -155,6 +154,7 @@ + @@ -245,9 +245,6 @@ - - $(DevEnvDir)\Microsoft.VisualStudio.CallHierarchy.Package.Definitions.dll - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.dll @@ -295,21 +292,6 @@ false - - - - - $(DevEnvDir)\CommonExtensions\Microsoft\Architecture Tools\GraphProviderPackage\Microsoft.VisualStudio.Progression.CodeSchema.dll - - - $(DevEnvDir)\CommonExtensions\Microsoft\Architecture Tools\GraphProviderPackage\Microsoft.VisualStudio.Progression.Common.dll - - - $(DevEnvDir)\CommonExtensions\Microsoft\Architecture Tools\GraphProviderPackage\Microsoft.VisualStudio.Progression.Interfaces.dll - - - false - diff --git a/src/VisualStudio/Core/Def/SymbolSearch/IAddReferenceDatabaseWrapper.cs b/src/VisualStudio/Core/Def/SymbolSearch/IAddReferenceDatabaseWrapper.cs new file mode 100644 index 0000000000000..bb3d82d1aa05a --- /dev/null +++ b/src/VisualStudio/Core/Def/SymbolSearch/IAddReferenceDatabaseWrapper.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Elfie.Model; + +namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch +{ + // Wrapper types to ensure we delay load the elfie database. + internal interface IAddReferenceDatabaseWrapper + { + AddReferenceDatabase Database { get; } + } + + internal class AddReferenceDatabaseWrapper : IAddReferenceDatabaseWrapper + { + public AddReferenceDatabase Database { get; } + + public AddReferenceDatabaseWrapper(AddReferenceDatabase database) + { + Database = database; + } + } +} diff --git a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs index e588990c77485..894bdef8002d1 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs @@ -44,8 +44,6 @@ internal partial class SymbolSearchService private const string MicrosoftAssemblyReferencesName = "MicrosoftAssemblyReferences"; private static readonly LinkedList s_log = new LinkedList(); - private readonly int _dataFormatVersion = AddReferenceDatabase.TextFileFormatVersion; - /// /// Cancellation support for the task we use to keep the local database up to date. /// When VS shuts down it will dispose us. We'll cancel the task at that point. @@ -58,9 +56,6 @@ internal partial class SymbolSearchService private readonly ConcurrentDictionary _sourceToUpdateSentinel = new ConcurrentDictionary(); - private readonly DirectoryInfo _cacheDirectoryInfo; - //private readonly FileInfo _databaseFileInfo; - // Interfaces that abstract out the external functionality we need. Used so we can easily // mock behavior during tests. private readonly IPackageInstallerService _installerService; @@ -70,6 +65,7 @@ internal partial class SymbolSearchService private readonly IRemoteControlService _remoteControlService; private readonly IPatchService _patchService; private readonly IDatabaseFactoryService _databaseFactoryService; + private readonly string _localSettingsDirectory; private readonly Func _reportAndSwallowException; public void Dispose() @@ -125,23 +121,26 @@ internal Task UpdateSourceInBackgroundAsync(string source) // We were the first ones to try to update this source. Spawn off a task to do // the updating. - return new Updater(this, source).UpdateInBackgroundAsync(); + return new Updater(this, source, _localSettingsDirectory).UpdateInBackgroundAsync(); } private class Updater { private readonly SymbolSearchService _service; private readonly string _source; + private readonly DirectoryInfo _cacheDirectoryInfo; private readonly FileInfo _databaseFileInfo; - public Updater(SymbolSearchService service, string source) + public Updater(SymbolSearchService service, string source, string localSettingsDirectory) { _service = service; _source = source; - var fileName = ConvertToFileName(source); + _cacheDirectoryInfo = new DirectoryInfo(Path.Combine( + localSettingsDirectory, "PackageCache", string.Format(Invariant($"Format{AddReferenceDatabase.TextFileFormatVersion}")))); + _databaseFileInfo = new FileInfo( - Path.Combine(_service._cacheDirectoryInfo.FullName, fileName + ".txt")); + Path.Combine(_cacheDirectoryInfo.FullName, ConvertToFileName(source) + ".txt")); } /// @@ -254,12 +253,12 @@ private void CleanCacheDirectory() _service.LogInfo("Cleaning cache directory"); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). - if (!_service._ioService.Exists(_service._cacheDirectoryInfo)) + if (!_service._ioService.Exists(_cacheDirectoryInfo)) { _service.LogInfo("Creating cache directory"); // (intentionally not wrapped in IOUtilities. If this throws we want to restart). - _service._ioService.Create(_service._cacheDirectoryInfo); + _service._ioService.Create(_cacheDirectoryInfo); _service.LogInfo("Cache directory created"); } @@ -268,7 +267,7 @@ private void CleanCacheDirectory() private async Task DownloadFullDatabaseAsync() { - var serverPath = Invariant($"Elfie_V{_service._dataFormatVersion}/Latest.xml"); + var serverPath = Invariant($"Elfie_V{AddReferenceDatabase.TextFileFormatVersion}/Latest.xml"); _service.LogInfo($"Downloading and processing full database: {serverPath}"); @@ -332,7 +331,7 @@ private async Task WriteDatabaseFile(byte[] bytes) () => { var guidString = Guid.NewGuid().ToString(); - var tempFilePath = Path.Combine(_service._cacheDirectoryInfo.FullName, guidString + ".tmp"); + var tempFilePath = Path.Combine(_cacheDirectoryInfo.FullName, guidString + ".tmp"); _service.LogInfo($"Temp file path: {tempFilePath}"); @@ -402,7 +401,7 @@ private async Task PatchLocalDatabaseAsync() var databaseVersion = database.DatabaseVersion; // Now attempt to download and apply patch file. - var serverPath = Invariant($"Elfie_V{_service._dataFormatVersion}/{database.DatabaseVersion}_Patch.xml"); + var serverPath = Invariant($"Elfie_V{AddReferenceDatabase.TextFileFormatVersion}/{database.DatabaseVersion}_Patch.xml"); _service.LogInfo("Downloading and processing patch file: " + serverPath); @@ -424,7 +423,7 @@ private async Task PatchLocalDatabaseAsync() private AddReferenceDatabase CreateAndSetInMemoryDatabase(byte[] bytes) { var database = CreateDatabaseFromBytes(bytes); - _service._sourceToDatabase[_source] = database; + _service._sourceToDatabase[_source] = new AddReferenceDatabaseWrapper(database); return database; } diff --git a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs index f2a30c81729e6..1da89cb57f852 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs @@ -36,7 +36,8 @@ internal partial class SymbolSearchService : ISymbolSearchService, IDisposable { - private ConcurrentDictionary _sourceToDatabase = new ConcurrentDictionary(); + private ConcurrentDictionary _sourceToDatabase = + new ConcurrentDictionary(); public SymbolSearchService( VSShell.SVsServiceProvider serviceProvider, @@ -104,12 +105,9 @@ private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServi _remoteControlService = remoteControlService; _patchService = patchService; _databaseFactoryService = databaseFactoryService; + _localSettingsDirectory = localSettingsDirectory; _reportAndSwallowException = reportAndSwallowException; - _cacheDirectoryInfo = new DirectoryInfo(Path.Combine( - localSettingsDirectory, "PackageCache", string.Format(Invariant($"Format{_dataFormatVersion}")))); - // _databaseFileInfo = new FileInfo(Path.Combine(_cacheDirectoryInfo.FullName, "NuGetCache.txt")); - _cancellationTokenSource = cancellationTokenSource; _cancellationToken = _cancellationTokenSource.Token; } @@ -117,13 +115,14 @@ private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServi public IEnumerable FindPackagesWithType( string source, string name, int arity, CancellationToken cancellationToken) { - AddReferenceDatabase database; - if (!_sourceToDatabase.TryGetValue(source, out database)) + IAddReferenceDatabaseWrapper databaseWrapper; + if (!_sourceToDatabase.TryGetValue(source, out databaseWrapper)) { // Don't have a database to search. yield break; } + var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. @@ -190,13 +189,14 @@ private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServi string name, int arity, CancellationToken cancellationToken) { // Our reference assembly data is stored in the nuget.org DB. - AddReferenceDatabase database; - if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out database)) + IAddReferenceDatabaseWrapper databaseWrapper; + if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out databaseWrapper)) { // Don't have a database to search. yield break; } + var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. diff --git a/src/VisualStudio/Core/Def/project.json b/src/VisualStudio/Core/Def/project.json index 27cf21c74e555..f95e0ad699efd 100644 --- a/src/VisualStudio/Core/Def/project.json +++ b/src/VisualStudio/Core/Def/project.json @@ -2,6 +2,9 @@ "dependencies": { "ManagedEsent": "1.9.2.0", "Microsoft.CodeAnalysis.Elfie": "0.10.6-rc2", + "RoslynDependencies.Microsoft.VisualStudio.Progression": "14.2.25123", + "RoslynDependencies.Microsoft.VisualStudio.GraphModel": "14.2.25123", + "RoslynDependencies.Microsoft.VisualStudio.CallHierarchy.Package.Definitions": "14.0.23107", "NuGet.VisualStudio": { "version": "3.3.0", "suppressParent": "all" diff --git a/src/VisualStudio/Core/Test/AnalyzerSupport/AnalyzerDependencyCheckerTests.vb b/src/VisualStudio/Core/Test/AnalyzerSupport/AnalyzerDependencyCheckerTests.vb index 0c926bbc2f848..c4c9d3d78b8be 100644 --- a/src/VisualStudio/Core/Test/AnalyzerSupport/AnalyzerDependencyCheckerTests.vb +++ b/src/VisualStudio/Core/Test/AnalyzerSupport/AnalyzerDependencyCheckerTests.vb @@ -9,6 +9,8 @@ Imports Microsoft.VisualStudio.LanguageServices.Implementation Imports Microsoft.Win32 Imports Roslyn.Test.Utilities +Imports System.FormattableString + Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Public Class AnalyzerDependencyCheckerTests Inherits TestBase @@ -17,7 +19,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Private Shared ReadOnly Property MSBuildDirectory As String Get If s_msbuildDirectory Is Nothing Then - Dim key = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0", False) + Dim vsVersion = If(Environment.GetEnvironmentVariable("VisualStudioVersion"), "14.0") + Dim key = Registry.LocalMachine.OpenSubKey(Invariant($"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}"), False) If key IsNot Nothing Then Dim toolsPath = key.GetValue("MSBuildToolsPath") diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index f86f3652ef6e7..4b986efd92773 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -13,7 +13,13 @@ AnyCPU v4.6 + true + + + $(VisualStudioReferenceAssemblyVersion) + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} @@ -125,13 +131,6 @@ false - - - - - - false - @@ -143,16 +142,6 @@ - - - - - false - - - @@ -402,4 +391,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs new file mode 100644 index 0000000000000..8115e68393410 --- /dev/null +++ b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Roslyn.Test.Utilities; +using Roslyn.VisualStudio.IntegrationTests; +using Roslyn.VisualStudio.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.CSharp.IntegrationTests +{ + [Collection(nameof(SharedIntegrationHostFixture))] + public class CSharpAutomaticBraceCompletion : IDisposable + { + private readonly VisualStudioInstanceContext _visualStudio; + private readonly Workspace _workspace; + private readonly Solution _solution; + private readonly Project _project; + private readonly EditorWindow _editorWindow; + + public CSharpAutomaticBraceCompletion(VisualStudioInstanceFactory instanceFactory) + { + _visualStudio = instanceFactory.GetNewOrUsedInstance(); + + _solution = _visualStudio.Instance.SolutionExplorer.CreateSolution(nameof(CSharpAutomaticBraceCompletion)); + _project = _solution.AddProject("TestProj", ProjectTemplate.ClassLibrary, ProjectLanguage.CSharp); + + _workspace = _visualStudio.Instance.Workspace; + _workspace.UseSuggestionMode = false; + + _editorWindow = _visualStudio.Instance.EditorWindow; + } + + public void Dispose() + { + _visualStudio.Dispose(); + } + + [Fact] + public async Task BracesInsertionAndTabCompleting() + { + _editorWindow.Text = @"class C { + void Foo() { + // Marker + } +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("if (true) {"); + + Assert.Equal(" if (true) { ", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal("}", _editorWindow.CurrentLineTextAfterCursor); + + await _editorWindow.TypeTextAsync($"{EditorWindow.TAB}"); + + Assert.Equal(" if (true) { }", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task BracesOvertyping() + { + _editorWindow.Text = @"class C { + void Foo() { + // Marker + } +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("if (true) {"); + await _editorWindow.TypeTextAsync("}"); + + Assert.Equal(" if (true) { }", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task BracesOnReturnNoFormattingOnlyIndentationBeforeCloseBrace() + { + _editorWindow.Text = @"class C { + void Foo() { + // Marker + } +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("if (true) {"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); + await _editorWindow.TypeTextAsync("var a = 1;"); + + Assert.Equal(" var a = 1;", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task BracesOnReturnOvertypingTheClosingBrace() + { + _editorWindow.Text = @"class C { + void Foo() { + // Marker + } +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("if (true) {"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); + await _editorWindow.TypeTextAsync("var a = 1;}"); + + Assert.Equal(" }", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + + Assert.Contains(@"if (true) + { + var a = 1; + } +", _editorWindow.Text); + } + + [Fact] + [WorkItem(653540, "DevDiv")] + public async Task BracesOnReturnWithNonWhitespaceSpanInside() + { + _editorWindow.Text = string.Empty; + + await _editorWindow.TypeTextAsync("class A { int i;"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); + + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal("}", _editorWindow.CurrentLineTextAfterCursor); + + Assert.Contains(@"class A { int i; +}", _editorWindow.Text); + } + + [Fact] + public async Task ParenInsertionAndTabCompleting() + { + _editorWindow.Text = @"class C { + //Marker +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("void Foo("); + + Assert.Equal(" void Foo(", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(")", _editorWindow.CurrentLineTextAfterCursor); + + await _editorWindow.TypeTextAsync("int x"); + await _editorWindow.TypeTextAsync($"{EditorWindow.TAB}"); + + Assert.Equal(" void Foo(int x)", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task ParenOvertyping() + { + _editorWindow.Text = @"class C { + //Marker +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("void Foo("); + await _editorWindow.TypeTextAsync($"{EditorWindow.ESC}"); + await _editorWindow.TypeTextAsync(")"); + + Assert.Equal(" void Foo()", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(")", _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task SquareBracketInsertion() + { + _editorWindow.Text = @"class C { + //Marker +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("int ["); + + Assert.Equal(" int [", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal("]", _editorWindow.CurrentLineTextAfterCursor); + } + + [Fact] + public async Task SquareBracketOvertyping() + { + _editorWindow.Text = @"class C { + //Marker +}"; + + _editorWindow.PlaceCursor("// Marker"); + + await _editorWindow.TypeTextAsync("int ["); + await _editorWindow.TypeTextAsync("]"); + + Assert.Equal(" int []", _editorWindow.CurrentLineTextBeforeCursor); + Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); + } + } +} diff --git a/src/VisualStudio/IntegrationTests/CSharp/CSharpBuild.cs b/src/VisualStudio/IntegrationTests/CSharp/CSharpBuild.cs index e18fb34a13cd1..d58125542997c 100644 --- a/src/VisualStudio/IntegrationTests/CSharp/CSharpBuild.cs +++ b/src/VisualStudio/IntegrationTests/CSharp/CSharpBuild.cs @@ -1,10 +1,11 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.Test.Utilities; using Xunit; -namespace Roslyn.VisualStudio.Integration.UnitTests +namespace Roslyn.VisualStudio.CSharp.IntegrationTests { [Collection(nameof(SharedIntegrationHostFixture))] public class CSharpBuild : IDisposable diff --git a/src/VisualStudio/IntegrationTests/CSharp/CSharpInteractiveDemo.cs b/src/VisualStudio/IntegrationTests/CSharp/CSharpInteractiveDemo.cs index b3e8156d916cf..6cbe3a359123e 100644 --- a/src/VisualStudio/IntegrationTests/CSharp/CSharpInteractiveDemo.cs +++ b/src/VisualStudio/IntegrationTests/CSharp/CSharpInteractiveDemo.cs @@ -2,10 +2,11 @@ using System; using System.Threading.Tasks; +using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.Test.Utilities; using Xunit; -namespace Roslyn.VisualStudio.Integration.UnitTests +namespace Roslyn.VisualStudio.CSharp.IntegrationTests { [Collection(nameof(SharedIntegrationHostFixture))] public class CSharpInteractiveDemo : IDisposable diff --git a/src/VisualStudio/IntegrationTests/SharedIntegrationHostFixture.cs b/src/VisualStudio/IntegrationTests/SharedIntegrationHostFixture.cs index 6df83be155393..0a59dcdcd6106 100644 --- a/src/VisualStudio/IntegrationTests/SharedIntegrationHostFixture.cs +++ b/src/VisualStudio/IntegrationTests/SharedIntegrationHostFixture.cs @@ -3,7 +3,7 @@ using Roslyn.VisualStudio.Test.Utilities; using Xunit; -namespace Roslyn.VisualStudio.Integration.UnitTests +namespace Roslyn.VisualStudio.IntegrationTests { [CollectionDefinition(nameof(SharedIntegrationHostFixture))] public sealed class SharedIntegrationHostFixture : ICollectionFixture diff --git a/src/VisualStudio/IntegrationTests/VisualBasic/BasicBuild.cs b/src/VisualStudio/IntegrationTests/VisualBasic/BasicBuild.cs index 5620f807cad14..66f6fc2662a4a 100644 --- a/src/VisualStudio/IntegrationTests/VisualBasic/BasicBuild.cs +++ b/src/VisualStudio/IntegrationTests/VisualBasic/BasicBuild.cs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.Test.Utilities; using Xunit; -namespace Roslyn.VisualStudio.Integration.UnitTests +namespace Roslyn.VisualStudio.Basic.IntegrationTests { [Collection(nameof(SharedIntegrationHostFixture))] public class BasicBuild diff --git a/src/VisualStudio/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTests/VisualStudioIntegrationTests.csproj index 94d50c204fb85..5999dfafbaefb 100644 --- a/src/VisualStudio/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -15,6 +15,7 @@ + @@ -31,6 +32,10 @@ {8635cb8f-d210-41ed-b4ff-71502cdb475c} xUnit.net + + {76c6f005-c89d-4348-bb4a-391898dbeb52} + TestUtilities.Desktop + {3BED15FD-D608-4573-B432-1569C1026F6D} VisualStudioTestUtilities diff --git a/src/VisualStudio/Setup.Next/AssemblyRedirects.cs b/src/VisualStudio/Setup.Next/AssemblyRedirects.cs new file mode 100644 index 0000000000000..f25c23bffe5c6 --- /dev/null +++ b/src/VisualStudio/Setup.Next/AssemblyRedirects.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Roslyn.VisualStudio.Setup; + +[assembly: ProvideRoslynBindingRedirection("Microsoft.CodeAnalysis.EditorFeatures.Next.dll")] +[assembly: ProvideBindingRedirection( + AssemblyName = "Microsoft.VisualStudio.CallHierarchy.Package.Definitions", + GenerateCodeBase = false, + PublicKeyToken = "31BF3856AD364E35", + OldVersionLowerBound = "14.0.0.0", + OldVersionUpperBound = "14.9.9.9", + NewVersion = "15.0.0.0")] \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj new file mode 100644 index 0000000000000..db8a237254fc3 --- /dev/null +++ b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj @@ -0,0 +1,71 @@ + + + + + + + Debug + AnyCPU + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {143FE684-6E1C-41DF-9C60-84C7772DC49C} + Library + Roslyn.VisualStudio.Setup.Next + Roslyn.VisualStudio.Setup.Next + true + true + RoslynDev + false + false + false + $(VisualStudioVersion) + true + v4.6 + true + + + + {8da861d8-0cce-4334-b6c0-01a846c881ec} + VisualStudio + False + + + {366BBCDC-B05F-4677-9B5B-78BA816A1484} + EditorFeatures.Next + BuiltProjectOutputGroup%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b + DebugSymbolsProjectOutputGroup%3b + + + VisualStudioSetup + false + + + + AnyCPU + + + AnyCPU + + + Program + $(DevEnvDir)devenv.exe + /rootsuffix RoslynDev /log + + + + + Designer + + + + + + + + ProvideRoslynBindingRedirection.cs + + + + + + + diff --git a/src/VisualStudio/Setup.Next/project.json b/src/VisualStudio/Setup.Next/project.json new file mode 100644 index 0000000000000..0b16291820b67 --- /dev/null +++ b/src/VisualStudio/Setup.Next/project.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + }, + "frameworks": { + "net46": { } + }, + "runtimes": { + "win7": { } + } +} diff --git a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest new file mode 100644 index 0000000000000..02a77efbd27d3 --- /dev/null +++ b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest @@ -0,0 +1,21 @@ + + + + + Roslyn Language Services for Visual Studio 15 + C# and VB.NET language services for Visual Studio 15. + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Setup/VisualStudioSetup.csproj b/src/VisualStudio/Setup/VisualStudioSetup.csproj index 04de9e3669042..8b0f1b02d782b 100644 --- a/src/VisualStudio/Setup/VisualStudioSetup.csproj +++ b/src/VisualStudio/Setup/VisualStudioSetup.csproj @@ -138,9 +138,6 @@ true - - - @@ -183,4 +180,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs new file mode 100644 index 0000000000000..45264ceb05ef1 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using EnvDTE; + +namespace Roslyn.VisualStudio.Test.Utilities +{ + public static class DteExtensions + { + public static async Task ExecuteCommandAsync(this DTE dte, string command, string args = "") + { + // args is "" the default because it is the default value used by Dte.ExecuteCommand and changing our default + // to something more logical, like null, would change the expected behavior of Dte.ExecuteCommand + + await dte.WaitForCommandAvailabilityAsync(command).ConfigureAwait(continueOnCapturedContext: false); + IntegrationHelper.RetryRpcCall(() => dte.ExecuteCommand(command, args)); + } + + public static Window LocateWindow(this DTE dte, string windowTitle) + { + var dteWindows = IntegrationHelper.RetryRpcCall(() => dte.Windows); + + foreach (Window window in dteWindows) + { + var windowCaption = IntegrationHelper.RetryRpcCall(() => window.Caption); + + if (windowCaption.Equals(windowTitle)) + { + return window; + } + } + return null; + } + + public static Task WaitForCommandAvailabilityAsync(this DTE dte, string command) + => IntegrationHelper.WaitForResultAsync(() => IntegrationHelper.RetryRpcCall(() => dte.Commands.Item(command).IsAvailable), expectedResult: true); + } +} diff --git a/src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs b/src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs new file mode 100644 index 0000000000000..a0a0d56e88c68 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Roslyn.VisualStudio.Test.Utilities +{ + internal static class IntegrationServiceExtensions + { + public static void Execute(this IntegrationService integrationService, Type type, string methodName, BindingFlags bindingFlags = (BindingFlags.Public | BindingFlags.Static), params object[] parameters) + => Execute(integrationService, type.Assembly.Location, type.FullName, methodName, bindingFlags, parameters); + + public static T Execute(this IntegrationService integrationService, Type type, string methodName, BindingFlags bindingFlags = (BindingFlags.Public | BindingFlags.Static), params object[] parameters) + => Execute(integrationService, type.Assembly.Location, type.FullName, methodName, bindingFlags, parameters); + + public static void Execute(this IntegrationService integrationService, string assemblyFilePath, string typeFullName, string methodName, BindingFlags bindingFlags = (BindingFlags.Public | BindingFlags.Static), params object[] parameters) + { + var result = integrationService.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); + + if (result != null) + { + throw new InvalidOperationException("The specified call was not expected to return a value."); + } + } + + public static T Execute(this IntegrationService integrationService, string assemblyFilePath, string typeFullName, string methodName, BindingFlags bindingFlags = (BindingFlags.Public | BindingFlags.Static), params object[] parameters) + { + var objectUri = integrationService.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); + + if (objectUri == null) + { + throw new InvalidOperationException("The specified call was expected to return a value."); + } + + return (T)(Activator.GetObject(typeof(T), $"{integrationService.Uri}/{objectUri}")); + } + } +} diff --git a/src/VisualStudio/TestUtilities/IntegrationHelper.cs b/src/VisualStudio/TestUtilities/IntegrationHelper.cs index 16d6df0e5b59c..95ee98862f1e2 100644 --- a/src/VisualStudio/TestUtilities/IntegrationHelper.cs +++ b/src/VisualStudio/TestUtilities/IntegrationHelper.cs @@ -23,6 +23,40 @@ namespace Roslyn.VisualStudio.Test.Utilities /// Provides some helper functions used by the other classes in the project. internal static class IntegrationHelper { + public static bool AttachThreadInput(uint idAttach, uint idAttachTo) + { + var success = User32.AttachThreadInput(idAttach, idAttachTo, true); + + if (!success) + { + var hresult = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(hresult); + } + + return success; + } + + public static bool BlockInput() + { + var success = User32.BlockInput(true); + + if (!success) + { + var hresult = Marshal.GetHRForLastWin32Error(); + + if (hresult == VSConstants.E_ACCESSDENIED) + { + Debug.WriteLine("Input cannot be blocked because the system requires Administrative privileges."); + } + else + { + Debug.WriteLine("Input cannot be blocked because another thread has blocked the input."); + } + } + + return success; + } + public static void CreateDirectory(string path, bool deleteExisting = false) { if (deleteExisting) @@ -41,6 +75,19 @@ public static void DeleteDirectoryRecursively(string path) } } + public static bool DetachThreadInput(uint idAttach, uint idAttachTo) + { + var success = User32.AttachThreadInput(idAttach, idAttachTo, false); + + if (!success) + { + var hresult = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(hresult); + } + + return success; + } + public static async Task DownloadFileAsync(string downloadUrl, string fileName) { using (var webClient = new WebClient()) @@ -49,12 +96,33 @@ public static async Task DownloadFileAsync(string downloadUrl, string fileName) } } + public static IntPtr GetForegroundWindow() + { + // Attempt to get the foreground window in a loop, as the User32 function can return IntPtr.Zero + // in certain circumstances, such as when a window is losing activation. + + var foregroundWindow = IntPtr.Zero; + + do + { + foregroundWindow = User32.GetForegroundWindow(); + } + while (foregroundWindow == IntPtr.Zero); + + return foregroundWindow; + } + /// Gets the Modal Window that is currently blocking interaction with the specified window or if none exists. public static IntPtr GetModalWindowFromParentWindow(IntPtr parentWindow) { foreach (var topLevelWindow in GetTopLevelWindows()) { - // Windows has several concepts of 'Parent Window', make sure we cover all ones for 'Modal Dialogs' + // GetParent will return the parent or owner of the specified window, unless: + // * The window is a top-level window that is unowned + // * The window is a top-level does not have the WS_POPUP style + // * The owner window has the WS_POPUP style + // GetWindow with GW_OWNER specified will return the owner window, but not the parent window + // GetAncestor with GA_PARENT specified will return the parent window, but not the owner window if ((User32.GetParent(topLevelWindow) == parentWindow) || (User32.GetWindow(topLevelWindow, User32.GW_OWNER) == parentWindow) || (User32.GetAncestor(topLevelWindow, User32.GA_PARENT) == parentWindow)) @@ -66,6 +134,19 @@ public static IntPtr GetModalWindowFromParentWindow(IntPtr parentWindow) return IntPtr.Zero; } + public static object GetRegistryKeyValue(RegistryKey baseKey, string subKeyName, string valueName) + { + using (var registryKey = baseKey.OpenSubKey(subKeyName)) + { + if (registryKey == null) + { + throw new Exception($@"The specified registry key could not be found. Registry Key: '{baseKey}\{subKeyName}'"); + } + + return registryKey.GetValue(valueName); + } + } + /// Gets the title text for the specified window. /// GetWindowText() does not work across the process boundary. public static string GetTitleForWindow(IntPtr window) @@ -91,7 +172,14 @@ public static IEnumerable GetTopLevelWindows() topLevelWindows.Add(hWnd); return true; }); - User32.EnumWindows(enumFunc, IntPtr.Zero); + + var success = User32.EnumWindows(enumFunc, IntPtr.Zero); + + if (!success) + { + var hresult = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(hresult); + } return topLevelWindows; } @@ -114,6 +202,108 @@ public static void KillProcess(string processName) } } + public static void RetryRpcCall(Action action) + { + do + { + try + { + action(); + return; + } + catch (COMException exception) when ((exception.HResult == VSConstants.RPC_E_CALL_REJECTED) || + (exception.HResult == VSConstants.RPC_E_SERVERCALL_RETRYLATER)) + { + // We'll just try again in this case + } + } + while (true); + } + + public static T RetryRpcCall(Func action) + { + T returnValue = default(T); + RetryRpcCall(() => { + returnValue = action(); + }); + return returnValue; + } + + public static void SetForegroundWindow(IntPtr window) + { + var foregroundWindow = GetForegroundWindow(); + + if (window == foregroundWindow) + { + return; + } + + var activeThreadId = User32.GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero); + var currentThreadId = Kernel32.GetCurrentThreadId(); + + bool threadInputsAttached = false; + + try + { + // Attach the thread inputs so that 'SetActiveWindow' and 'SetFocus' work + threadInputsAttached = AttachThreadInput(currentThreadId, activeThreadId); + + // Make the window a top-most window so it will appear above any existing top-most windows + User32.SetWindowPos(window, (IntPtr)(User32.HWND_TOPMOST), 0, 0, 0, 0, (User32.SWP_NOSIZE | User32.SWP_NOMOVE)); + + // Move the window into the foreground as it may not have been achieved by the 'SetWindowPos' call + var success = User32.SetForegroundWindow(window); + + if (!success) + { + throw new InvalidOperationException("Setting the foreground window failed."); + } + + // Ensure the window is 'Active' as it may not have been achieved by 'SetForegroundWindow' + User32.SetActiveWindow(window); + + // Give the window the keyboard focus as it may not have been achieved by 'SetActiveWindow' + User32.SetFocus(window); + + // Remove the 'Top-Most' qualification from the window + User32.SetWindowPos(window, (IntPtr)(User32.HWND_NOTOPMOST), 0, 0, 0, 0, (User32.SWP_NOSIZE | User32.SWP_NOMOVE)); + } + finally + { + if (threadInputsAttached) + { + // Finally, detach the thread inputs from eachother + DetachThreadInput(currentThreadId, activeThreadId); + } + } + } + + public static void SendInput(User32.INPUT[] input) + { + var eventsInserted = User32.SendInput((uint)(input.Length), input, User32.SizeOf_INPUT); + + if (eventsInserted == 0) + { + var hresult = Marshal.GetHRForLastWin32Error(); + throw new ExternalException("Sending input failed because input was blocked by another thread.", hresult); + } + } + + public static bool TryDeleteDirectoryRecursively(string path) + { + try + { + DeleteDirectoryRecursively(path); + return true; + } + catch (Exception e) + { + Debug.WriteLine($"Warning: Failed to recursively delete the specified directory. (Name: '{path}')"); + Debug.WriteLine($"\t{e}"); + return false; + } + } + /// Locates the DTE object for the specified process. public static DTE TryLocateDteForProcess(Process process) { @@ -172,31 +362,14 @@ public static DTE TryLocateDteForProcess(Process process) return (DTE)(dte); } - public static object GetRegistryKeyValue(RegistryKey baseKey, string subKeyName, string valueName) + public static void UnblockInput() { - using (var registryKey = baseKey.OpenSubKey(subKeyName)) - { - if (registryKey == null) - { - throw new Exception($"The specified registry key could not be found. Registry Key: '{registryKey}'"); - } - - return registryKey.GetValue(valueName); - } - } + var success = User32.BlockInput(false); - public static bool TryDeleteDirectoryRecursively(string path) - { - try + if (!success) { - DeleteDirectoryRecursively(path); - return true; - } - catch (Exception e) - { - Debug.WriteLine($"Warning: Failed to recursively delete the specified directory. (Name: '{path}')"); - Debug.WriteLine($"\t{e}"); - return false; + var hresult = Marshal.GetHRForLastWin32Error(); + throw new ExternalException("Input cannot be unblocked because it was blocked by another thread.", hresult); } } @@ -208,29 +381,6 @@ public static async Task WaitForResultAsync(Func action, T expectedResult) } } - public static void RetryDteCall(Action action) - { - while (true) - { - try - { - action(); - return; - } - catch (COMException exception) when (exception.HResult == VSConstants.RPC_E_CALL_REJECTED) - { - // We'll just try again in this case - } - } - } - - public static T RetryDteCall(Func action) - { - T returnValue = default(T); - RetryDteCall(() => { returnValue = action(); }); - return returnValue; - } - public static async Task WaitForNotNullAsync(Func action) where T : class { var result = action(); diff --git a/src/VisualStudio/TestUtilities/IntegrationService.cs b/src/VisualStudio/TestUtilities/IntegrationService.cs index 86a7f13a0fd87..9d4b222203aab 100644 --- a/src/VisualStudio/TestUtilities/IntegrationService.cs +++ b/src/VisualStudio/TestUtilities/IntegrationService.cs @@ -14,7 +14,7 @@ internal class IntegrationService : MarshalByRefObject // Make the channel name well known by using a static base and appending the process ID of the host public static readonly string PortNameFormatString = $"{nameof(IntegrationService)}_{{0}}"; - private ConcurrentDictionary _marshalledObjects = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _marshalledObjects = new ConcurrentDictionary(); public string Execute(string assemblyFilePath, string typeFullName, string methodName, BindingFlags bindingFlags, params object[] parameters) { @@ -41,5 +41,9 @@ public string Execute(string assemblyFilePath, string typeFullName, string metho return objectUri; } + + /// The base Uri of the service. + /// This resolves to a string such as ipc://IntegrationService_{HostProcessId}" + public string Uri { get; set; } } } diff --git a/src/VisualStudio/TestUtilities/Interop/Kernel32.cs b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs new file mode 100644 index 0000000000000..1d9d1a7e738a1 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.InteropServices; + +namespace Roslyn.VisualStudio.Test.Utilities.Interop +{ + internal static class Kernel32 + { + [DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetCurrentThreadId", PreserveSig = true, SetLastError = false)] + public static extern uint GetCurrentThreadId(); + } +} diff --git a/src/VisualStudio/TestUtilities/Interop/User32.cs b/src/VisualStudio/TestUtilities/Interop/User32.cs index a744b6e2c8b0c..3fab7a12a3fcd 100644 --- a/src/VisualStudio/TestUtilities/Interop/User32.cs +++ b/src/VisualStudio/TestUtilities/Interop/User32.cs @@ -8,6 +8,8 @@ namespace Roslyn.VisualStudio.Test.Utilities.Interop { internal static class User32 { + public static readonly int SizeOf_INPUT = Marshal.SizeOf(); + public const uint GA_PARENT = 1; public const uint GA_ROOT = 2; public const uint GA_ROOTOWNER = 3; @@ -20,9 +22,101 @@ internal static class User32 public const uint GW_CHILD = 5; public const uint GW_ENABLEDPOPUP = 6; + public const int HWND_NOTOPMOST = -2; + public const int HWND_TOPMOST = -1; + public const int HWND_TOP = 0; + public const int HWND_BOTTOM = 1; + + public const uint INPUT_MOUSE = 0; + public const uint INPUT_KEYBOARD = 1; + public const uint INPUT_HARDWARE = 2; + + public const uint KEYEVENTF_NONE = 0x0000; + public const uint KEYEVENTF_EXTENDEDKEY = 0x0001; + public const uint KEYEVENTF_KEYUP = 0x0002; + public const uint KEYEVENTF_UNICODE = 0x0004; + public const uint KEYEVENTF_SCANCODE = 0x0008; + + public const uint SWP_NOSIZE = 0x0001; + public const uint SWP_NOMOVE = 0x0002; + public const uint SWP_NOZORDER = 0x0004; + public const uint SWP_NOREDRAW = 0x008; + public const uint SWP_NOACTIVATE = 0x0010; + public const uint SWP_DRAWFRAME = 0x0020; + public const uint SWP_FRAMECHANGED = 0x0020; + public const uint SWP_SHOWWINDOW = 0x0040; + public const uint SWP_HIDEWINDOW = 0x0080; + public const uint SWP_NOCOPYBITS = 0x0100; + public const uint SWP_NOOWNERZORDER = 0x0200; + public const uint SWP_NOREPOSITION = 0x0200; + public const uint SWP_NOSENDCHANGING = 0x0400; + public const uint SWP_DEFERERASE = 0x2000; + public const uint SWP_ASYNCWINDOWPOS = 0x4000; + + public const ushort VK_SHIFT = 0x0010; + public const ushort VK_CONTROL = 0x0011; + public const ushort VK_MENU = 0x0012; + + public const ushort VK_PRIOR = 0x0021; + public const ushort VK_NEXT = 0x0022; + public const ushort VK_END = 0x0023; + public const ushort VK_HOME = 0x0024; + public const ushort VK_LEFT = 0x0025; + public const ushort VK_UP = 0x0026; + public const ushort VK_RIGHT = 0x0027; + public const ushort VK_DOWN = 0x0028; + + public const ushort VK_INSERT = 0x002D; + public const ushort VK_DELETE = 0x002E; + public const uint WM_GETTEXT = 0x000D; public const uint WM_GETTEXTLENGTH = 0x000E; + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8)] + public struct INPUT + { + [FieldOffset(0)] + public uint Type; + + [FieldOffset(4)] + public MOUSEINPUT mi; + + [FieldOffset(4)] + public KEYBDINPUT ki; + + [FieldOffset(4)] + public HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)] + public struct HARDWAREINPUT + { + public uint uMsg; + public ushort wParamL; + public ushort wParamH; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)] + public struct KEYBDINPUT + { + public ushort wVk; + public ushort wScan; + public uint dwFlags; + public uint time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)] + public struct MOUSEINPUT + { + public int dx; + public int dy; + public uint mouseData; + public uint dwFlags; + public uint time; + public IntPtr dwExtraInfo; + } + [UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public delegate bool WNDENUMPROC( @@ -30,10 +124,24 @@ internal static class User32 [In] IntPtr lParam ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "AttachThreadInput", PreserveSig = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AttachThreadInput( + [In] uint idAttach, + [In] uint idAttachTo, + [In, MarshalAs(UnmanagedType.Bool)] bool fAttach + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "BlockInput", PreserveSig = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BlockInput( + [In, MarshalAs(UnmanagedType.Bool)] bool fBlockIt + ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "EnumWindows", PreserveSig = true, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool EnumWindows( - [In] WNDENUMPROC lpEnumFunc, + [In, MarshalAs(UnmanagedType.FunctionPtr)] WNDENUMPROC lpEnumFunc, [In] IntPtr lParam ); @@ -43,6 +151,10 @@ internal static class User32 [In] uint gaFlags ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetForegroundWindow", PreserveSig = true, SetLastError = false)] + public static extern IntPtr GetForegroundWindow( + ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetParent", PreserveSig = true, SetLastError = true)] public static extern IntPtr GetParent( [In] IntPtr hWnd @@ -54,6 +166,25 @@ internal static class User32 [In] uint uCmd ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetWindowThreadProcessId", PreserveSig = true, SetLastError = false)] + public static extern uint GetWindowThreadProcessId( + [In] IntPtr hWnd, + [Out, Optional] IntPtr lpdwProcessId + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetWindowThreadProcessId", PreserveSig = true, SetLastError = false)] + public static extern uint GetWindowThreadProcessId( + [In] IntPtr hWnd, + [Out, Optional] out uint lpdwProcessId + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SendInput", PreserveSig = true, SetLastError = true)] + public static extern uint SendInput( + [In] uint nInputs, + [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] INPUT[] pInputs, + [In] int cbSize + ); + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SendMessageW", PreserveSig = true, SetLastError = true)] public static extern IntPtr SendMessage( [In] IntPtr hWnd, @@ -69,5 +200,38 @@ internal static class User32 [In] IntPtr wParam, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SetActiveWindow", PreserveSig = true, SetLastError = true)] + public static extern IntPtr SetActiveWindow( + [In] IntPtr hWnd + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SetFocus", PreserveSig = true, SetLastError = true)] + public static extern IntPtr SetFocus( + [In] IntPtr hWnd + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SetForegroundWindow", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetForegroundWindow( + [In] IntPtr hWnd + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "SetWindowPos", PreserveSig = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos( + [In] IntPtr hWnd, + [In, Optional] IntPtr hWndInsertAfter, + [In] int X, + [In] int Y, + [In] int cx, + [In] int cy, + [In] uint uFlags + ); + + [DllImport("User32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "VkKeyScanW", PreserveSig = true, SetLastError = false)] + public static extern short VkKeyScan( + [In] char ch + ); } } diff --git a/src/VisualStudio/TestUtilities/Remoting/EditorWindowWrapper.cs b/src/VisualStudio/TestUtilities/Remoting/EditorWindowWrapper.cs new file mode 100644 index 0000000000000..6bab91737ab1a --- /dev/null +++ b/src/VisualStudio/TestUtilities/Remoting/EditorWindowWrapper.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Roslyn.VisualStudio.Test.Utilities.Remoting +{ + internal class EditorWindowWrapper : MarshalByRefObject + { + public static EditorWindowWrapper Create() => new EditorWindowWrapper(); + + private EditorWindowWrapper() + { + } + + public string Contents + { + get + { + return RemotingHelper.ActiveTextViewContents; + } + + set + { + RemotingHelper.ActiveTextViewContents = value; + } + } + } +} diff --git a/src/VisualStudio/TestUtilities/Remoting/InteractiveWindowWrapper.cs b/src/VisualStudio/TestUtilities/Remoting/InteractiveWindowWrapper.cs index 5969a6b17a308..aa31c5789f163 100644 --- a/src/VisualStudio/TestUtilities/Remoting/InteractiveWindowWrapper.cs +++ b/src/VisualStudio/TestUtilities/Remoting/InteractiveWindowWrapper.cs @@ -9,9 +9,11 @@ namespace Roslyn.VisualStudio.Test.Utilities.Remoting /// This object exists in the Visual Studio host and is marhsalled across the process boundary. internal class InteractiveWindowWrapper : MarshalByRefObject { - private IInteractiveWindow _interactiveWindow; + private readonly IInteractiveWindow _interactiveWindow; - public InteractiveWindowWrapper(IInteractiveWindow interactiveWindow) + public static InteractiveWindowWrapper CreateForCSharp() => new InteractiveWindowWrapper(RemotingHelper.CSharpInteractiveWindow); + + private InteractiveWindowWrapper(IInteractiveWindow interactiveWindow) { _interactiveWindow = interactiveWindow; } diff --git a/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs b/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs index caf3e22fcb1c7..b281efe756935 100644 --- a/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs +++ b/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs @@ -1,16 +1,28 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.ObjectModel; +using System.ComponentModel.Composition.Hosting; +using System.Linq; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Threading; -using Microsoft.VisualStudio; +using EnvDTE; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.InteractiveWindow; +using Microsoft.VisualStudio.InteractiveWindow.Shell; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices.CSharp.Interactive; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; +using Roslyn.Hosting.Diagnostics.Waiters; namespace Roslyn.VisualStudio.Test.Utilities.Remoting { @@ -18,69 +30,142 @@ namespace Roslyn.VisualStudio.Test.Utilities.Remoting /// This methods should be executed Visual Studio host via the method. internal static class RemotingHelper { - private static Guid IWpfTextViewId = new Guid("8C40265E-9FDB-4F54-A0FD-EBB72B7D0476"); + private static readonly Guid IWpfTextViewId = new Guid("8C40265E-9FDB-4F54-A0FD-EBB72B7D0476"); + private static readonly Guid RoslynPackageId = new Guid("6cf2e545-6109-4730-8883-cf43d7aec3e1"); - public static InteractiveWindowWrapper CreateCSharpInteractiveWindowWrapper() - { - var componentModel = (IComponentModel)(ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel))); - var vsInteractiveWindowProvider = componentModel.GetService(); - var vsInteractiveWindow = ExecuteOnUIThread(() => vsInteractiveWindowProvider.Open(0, true)); - return new InteractiveWindowWrapper(vsInteractiveWindow.InteractiveWindow); - } + private static readonly string[] SupportedLanguages = new string[] { LanguageNames.CSharp, LanguageNames.VisualBasic }; - public static string GetActiveTextViewContents() + public static string ActiveTextViewContents { - var textViewHost = GetActiveTextViewHost(); - return ExecuteOnUIThread(textViewHost.TextView.TextSnapshot.GetText); + get + { + return InvokeOnUIThread(ActiveTextView.TextSnapshot.GetText); + } + + set + { + InvokeOnUIThread(() => { + var textSnapshot = ActiveTextView.TextSnapshot; + var replacementSpan = new SnapshotSpan(textSnapshot, 0, textSnapshot.Length); + ActiveTextView.TextBuffer.Replace(replacementSpan, value); + }); + } } - public static void SetActiveTextViewContents(string text) + public static ReadOnlyCollection ActiveTextViewCompletionSessions => CompletionBroker.GetSessions(ActiveTextView); + + public static IComponentModel ComponentModel => GetGlobalService(typeof(SComponentModel)); + + public static IInteractiveWindow CSharpInteractiveWindow => CSharpVsInteractiveWindow.InteractiveWindow; + + public static VisualStudioWorkspace VisualStudioWorkspace => ComponentModel.GetService(); + + private static ITextView ActiveTextView => ActiveTextViewHost.TextView; + + private static IWpfTextViewHost ActiveTextViewHost { - var textViewHost = GetActiveTextViewHost(); - ExecuteOnUIThread(() => { - var textView = textViewHost.TextView; - var textSnapshot = textView.TextSnapshot; - var replacementSpan = new SnapshotSpan(textSnapshot, 0, textSnapshot.Length); - textView.TextBuffer.Replace(replacementSpan, text); - }); + get + { + // The active text view might not have finished composing yet, waiting for the application to 'idle' + // means that it is done pumping messages (including WM_PAINT) and the window should return the correct text view + WaitForApplicationIdle(); + + var activeVsTextView = (IVsUserData)(VsTextManagerActiveView); + + var wpfTextViewId = IWpfTextViewId; + object wpfTextViewHost = null; + + var hresult = activeVsTextView.GetData(ref wpfTextViewId, out wpfTextViewHost); + Marshal.ThrowExceptionForHR(hresult); + + return (IWpfTextViewHost)(wpfTextViewHost); + } } - private static void ExecuteOnUIThread(Action action) - => Application.Current.Dispatcher.Invoke(action); + private static ICompletionBroker CompletionBroker => ComponentModel.GetService(); + + private static IVsInteractiveWindow CSharpVsInteractiveWindow => InvokeOnUIThread(() => CSharpVsInteractiveWindowProvider.Open(0, true)); + + private static CSharpVsInteractiveWindowProvider CSharpVsInteractiveWindowProvider => ComponentModel.GetService(); + + private static Application CurrentApplication => Application.Current; + + private static Dispatcher CurrentApplicationDispatcher => CurrentApplication.Dispatcher; + + private static ExportProvider DefaultComponentModelExportProvider => ComponentModel.DefaultExportProvider; - private static T ExecuteOnUIThread(Func action) - => Application.Current.Dispatcher.Invoke(action); + public static DTE DTE => GetGlobalService(typeof(SDTE)); - private static IWpfTextViewHost GetActiveTextViewHost() + private static ServiceProvider GlobalServiceProvider => ServiceProvider.GlobalProvider; + + private static HostWorkspaceServices VisualStudioWorkspaceServices => VisualStudioWorkspace.Services; + + private static IVsShell VsShell => GetGlobalService(typeof(SVsShell)); + + private static IVsTextManager VsTextManager => GetGlobalService(typeof(SVsTextManager)); + + private static IVsTextView VsTextManagerActiveView { - // The active text view might not have finished composing yet, waiting for the application to 'idle' - // means that it is done pumping messages (including WM_PAINT) and the window should have finished composing - WaitForApplicationIdle(); + get + { + IVsTextView vsTextView = null; - var vsTextManager = (IVsTextManager)(ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager))); + var hresult = VsTextManager.GetActiveView(fMustHaveFocus: 1, pBuffer: null, ppView: out vsTextView); + Marshal.ThrowExceptionForHR(hresult); - IVsTextView vsTextView = null; - var hresult = vsTextManager.GetActiveView(fMustHaveFocus: 1, pBuffer: null, ppView: out vsTextView); + return vsTextView; + } + } + + private static TestingOnly_WaitingService WaitingService => DefaultComponentModelExportProvider.GetExport().Value; - if (hresult != VSConstants.S_OK) + public static void ActivateMainWindow() => InvokeOnUIThread(() => + { + var activeVisualStudioWindow = (IntPtr)(IntegrationHelper.RetryRpcCall(() => DTE.ActiveWindow.HWnd)); + + if (activeVisualStudioWindow == IntPtr.Zero) { - throw Marshal.GetExceptionForHR(hresult); + activeVisualStudioWindow = (IntPtr)(IntegrationHelper.RetryRpcCall(() => DTE.MainWindow.HWnd)); } - var vsUserData = (IVsUserData)(vsTextView); + IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow); + }); - object wpfTextViewHost = null; - vsUserData.GetData(ref IWpfTextViewId, out wpfTextViewHost); + public static void CleanupWaitingService() + { + var asynchronousOperationWaiterExports = DefaultComponentModelExportProvider.GetExports(); - if (hresult != VSConstants.S_OK) + if (!asynchronousOperationWaiterExports.Any()) { - throw Marshal.GetExceptionForHR(hresult); + throw new InvalidOperationException("The test waiting service could not be located."); } - return (IWpfTextViewHost)(wpfTextViewHost); + WaitingService.EnableActiveTokenTracking(true); } - private static void WaitForApplicationIdle() - => Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); + public static void CleanupWorkspace() + { + LoadRoslynPackage(); + VisualStudioWorkspace.TestHookPartialSolutionsDisabled = true; + } + + public static void WaitForSystemIdle() => CurrentApplicationDispatcher.Invoke(() => { }, DispatcherPriority.SystemIdle); + + public static void WaitForApplicationIdle() => CurrentApplicationDispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); + + private static T GetGlobalService(Type serviceType) => InvokeOnUIThread(() => (T)(GlobalServiceProvider.GetService(serviceType))); + + private static void InvokeOnUIThread(Action action) => CurrentApplicationDispatcher.Invoke(action); + + private static T InvokeOnUIThread(Func action) => CurrentApplicationDispatcher.Invoke(action); + + private static void LoadRoslynPackage() + { + var roslynPackageGuid = RoslynPackageId; + IVsPackage roslynPackage = null; + + var hresult = VsShell.LoadPackage(ref roslynPackageGuid, out roslynPackage); + Marshal.ThrowExceptionForHR(hresult); + } } } diff --git a/src/VisualStudio/TestUtilities/Remoting/WorkspaceWrapper.cs b/src/VisualStudio/TestUtilities/Remoting/WorkspaceWrapper.cs new file mode 100644 index 0000000000000..6dc73393d1557 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Remoting/WorkspaceWrapper.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis.Editor.Options; +using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.LanguageServices; + +namespace Roslyn.VisualStudio.Test.Utilities.Remoting +{ + internal class WorkspaceWrapper : MarshalByRefObject + { + private readonly VisualStudioWorkspace _workspace; + + public static WorkspaceWrapper Create() + { + var visualStudioWorkspace = RemotingHelper.VisualStudioWorkspace; + return new WorkspaceWrapper(visualStudioWorkspace); + } + + private WorkspaceWrapper(VisualStudioWorkspace workspace) + { + _workspace = workspace; + } + + public bool UseSuggestionMode + { + get + { + return Options.GetOption(EditorCompletionOptions.UseSuggestionMode); + } + + set + { + if (value != UseSuggestionMode) + { + RemotingHelper.DTE.ExecuteCommandAsync("Edit.ToggleCompletionMode").GetAwaiter().GetResult(); + } + } + } + + private OptionSet Options + { + get + { + return _workspace.Options; + } + + set + { + _workspace.Options = value; + } + } + } +} diff --git a/src/VisualStudio/TestUtilities/VisualStudioCommandNames.cs b/src/VisualStudio/TestUtilities/VisualStudioCommandNames.cs index 235d77aaae247..3fc7f415963d8 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioCommandNames.cs +++ b/src/VisualStudio/TestUtilities/VisualStudioCommandNames.cs @@ -1,11 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Roslyn.VisualStudio.Test.Utilities { internal static class VisualStudioCommandNames diff --git a/src/VisualStudio/TestUtilities/VisualStudioInstance.cs b/src/VisualStudio/TestUtilities/VisualStudioInstance.cs index c1c019ddc8a32..e7570d1eb8d43 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioInstance.cs +++ b/src/VisualStudio/TestUtilities/VisualStudioInstance.cs @@ -2,15 +2,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using System.Threading.Tasks; using System.Windows.Automation; using EnvDTE; +using Roslyn.VisualStudio.Test.Utilities.Remoting; using Process = System.Diagnostics.Process; @@ -20,42 +18,58 @@ public class VisualStudioInstance { private readonly Process _hostProcess; private readonly DTE _dte; - private readonly IntegrationService _service; - private readonly string _serviceUri; - private readonly IpcClientChannel _serviceChannel; + private readonly IntegrationService _integrationService; + private readonly IpcClientChannel _integrationServiceChannel; // TODO: We could probably expose all the windows/services/features of the host process in a better manner - private readonly Lazy _csharpInteractiveWindow; + private readonly Lazy _csharpInteractiveWindow; private readonly Lazy _editorWindow; private readonly Lazy _solutionExplorer; + private readonly Lazy _workspace; public VisualStudioInstance(Process process, DTE dte) { _hostProcess = process; _dte = dte; - ExecuteDteCommandAsync("Tools.StartIntegrationTestService").GetAwaiter().GetResult(); + dte.ExecuteCommandAsync(VisualStudioCommandNames.VsStartServiceCommand).GetAwaiter().GetResult(); - _serviceChannel = new IpcClientChannel($"ipc channel client for {_hostProcess.Id}", sinkProvider: null); - ChannelServices.RegisterChannel(_serviceChannel, ensureSecurity: true); + _integrationServiceChannel = new IpcClientChannel($"IPC channel client for {_hostProcess.Id}", sinkProvider: null); + ChannelServices.RegisterChannel(_integrationServiceChannel, ensureSecurity: true); // Connect to a 'well defined, shouldn't conflict' IPC channel - _serviceUri = string.Format($"ipc://{IntegrationService.PortNameFormatString}", _hostProcess.Id); - _service = (IntegrationService)(Activator.GetObject(typeof(IntegrationService), $"{_serviceUri}/{typeof(IntegrationService).FullName}")); + var serviceUri = string.Format($"ipc://{IntegrationService.PortNameFormatString}", _hostProcess.Id); + _integrationService = (IntegrationService)(Activator.GetObject(typeof(IntegrationService), $"{serviceUri}/{typeof(IntegrationService).FullName}")); + _integrationService.Uri = serviceUri; - _csharpInteractiveWindow = new Lazy(() => InteractiveWindow.CreateCSharpInteractiveWindow(this)); + // There is a lot of VS initialization code that goes on, so we want to wait for that to 'settle' before + // we start executing any actual code. + _integrationService.Execute(typeof(RemotingHelper), nameof(RemotingHelper.WaitForSystemIdle)); + + _csharpInteractiveWindow = new Lazy(() => new CSharpInteractiveWindow(this)); _editorWindow = new Lazy(() => new EditorWindow(this)); _solutionExplorer = new Lazy(() => new SolutionExplorer(this)); + _workspace = new Lazy(() => new Workspace(this)); + + // Ensure we are in a known 'good' state by cleaning up anything changed by the previous instance + Cleanup(); } public DTE Dte => _dte; public bool IsRunning => !_hostProcess.HasExited; - public InteractiveWindow CSharpInteractiveWindow => _csharpInteractiveWindow.Value; + public CSharpInteractiveWindow CSharpInteractiveWindow => _csharpInteractiveWindow.Value; + public EditorWindow EditorWindow => _editorWindow.Value; + public SolutionExplorer SolutionExplorer => _solutionExplorer.Value; + public Workspace Workspace => _workspace.Value; + + internal IntegrationService IntegrationService => _integrationService; + + #region Automation Elements public async Task ClickAutomationElementAsync(string elementName, bool recursive = false) { var automationElement = await LocateAutomationElementAsync(elementName, recursive).ConfigureAwait(continueOnCapturedContext: false); @@ -67,31 +81,7 @@ public async Task ClickAutomationElementAsync(string elementName, bool recursive } } - internal async Task ExecuteDteCommandAsync(string command, string args = "") - { - // args is "" by default because thats what Dte.ExecuteCommand does by default and changing our default - // to something more logical, like null, would change the expected behavior of Dte.ExecuteCommand - - await WaitForDteCommandAvailabilityAsync(command).ConfigureAwait(continueOnCapturedContext: false); - IntegrationHelper.RetryDteCall(() => _dte.ExecuteCommand(command, args)); - } - - internal T ExecuteOnHostProcess(Type type, string methodName, BindingFlags bindingFlags, params object[] parameters) - => ExecuteOnHostProcess(type.Assembly.Location, type.FullName, methodName, bindingFlags, parameters); - - internal T ExecuteOnHostProcess(string assemblyFilePath, string typeFullName, string methodName, BindingFlags bindingFlags, params object[] parameters) - { - var objectUri = _service.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); - - if (string.IsNullOrWhiteSpace(objectUri)) - { - return default(T); - } - - return (T)(Activator.GetObject(typeof(T), $"{_serviceUri}/{objectUri}")); - } - - internal async Task LocateAutomationElementAsync(string elementName, bool recursive = false) + private async Task LocateAutomationElementAsync(string elementName, bool recursive = false) { AutomationElement automationElement = null; var scope = (recursive ? TreeScope.Descendants : TreeScope.Children); @@ -105,63 +95,58 @@ internal async Task LocateAutomationElementAsync(string eleme return automationElement; } + #endregion - internal Task LocateDteWindowAsync(string windowTitle) - => IntegrationHelper.WaitForNotNullAsync(() => IntegrationHelper.RetryDteCall(() => - { - foreach (Window window in _dte.Windows) - { - if (window.Caption.Equals(windowTitle, StringComparison.OrdinalIgnoreCase)) - { - return window; - } - } - return null; - })); - - internal Task WaitForDteCommandAvailabilityAsync(string command) - => IntegrationHelper.WaitForResultAsync(() => IntegrationHelper.RetryDteCall(() => Dte.Commands.Item(command).IsAvailable), expectedResult: true); - - public void Close() + #region Cleanup + public void Cleanup() { - if (!IsRunning) - { - return; - } + CleanupOpenSolution(); + CleanupInteractiveWindow(); + CleanupWaitingService(); + CleanupWorkspace(); + } - CloseAndDeleteOpenSolution(); - CleanupRemotingService(); - CleanupHostProcess(); + private void CleanupInteractiveWindow() + { + var csharpInteractiveWindow = _dte.LocateWindow(CSharpInteractiveWindow.DteWindowTitle); + IntegrationHelper.RetryRpcCall(() => csharpInteractiveWindow?.Close()); } - public void CloseAndDeleteOpenSolution() + private void CleanupOpenSolution() { - IntegrationHelper.RetryDteCall(() => _dte.Documents.CloseAll(EnvDTE.vsSaveChanges.vsSaveChangesNo)); + IntegrationHelper.RetryRpcCall(() => _dte.Documents.CloseAll(vsSaveChanges.vsSaveChangesNo)); - if (IntegrationHelper.RetryDteCall(() => _dte.Solution) != null) + var dteSolution = IntegrationHelper.RetryRpcCall(() => _dte.Solution); + + if (dteSolution != null) { - var directoriesToDelete = IntegrationHelper.RetryDteCall(() => + var directoriesToDelete = IntegrationHelper.RetryRpcCall(() => { var directoryList = new List(); + var dteSolutionProjects = IntegrationHelper.RetryRpcCall(() => dteSolution.Projects); + // Save the full path to each project in the solution. This is so we can cleanup any folders after the solution is closed. - foreach (EnvDTE.Project project in _dte.Solution.Projects) + foreach (EnvDTE.Project project in dteSolutionProjects) { - directoryList.Add(Path.GetDirectoryName(project.FullName)); + var projectFullName = IntegrationHelper.RetryRpcCall(() => project.FullName); + directoryList.Add(Path.GetDirectoryName(projectFullName)); } // Save the full path to the solution. This is so we can cleanup any folders after the solution is closed. // The solution might be zero-impact and thus has no name, so deal with that - if (!string.IsNullOrEmpty(_dte.Solution.FullName)) + var dteSolutionFullName = IntegrationHelper.RetryRpcCall(() => dteSolution.FullName); + + if (!string.IsNullOrEmpty(dteSolutionFullName)) { - directoryList.Add(Path.GetDirectoryName(_dte.Solution.FullName)); + directoryList.Add(Path.GetDirectoryName(dteSolutionFullName)); } return directoryList; }); - IntegrationHelper.RetryDteCall(() => _dte.Solution.Close(SaveFirst: false)); + IntegrationHelper.RetryRpcCall(() => dteSolution.Close(SaveFirst: false)); foreach (var directoryToDelete in directoriesToDelete) { @@ -170,29 +155,48 @@ public void CloseAndDeleteOpenSolution() } } - private void CleanupHostProcess() + private void CleanupWaitingService() => _integrationService.Execute(typeof(RemotingHelper), nameof(RemotingHelper.CleanupWaitingService)); + + private void CleanupWorkspace() => _integrationService.Execute(typeof(RemotingHelper), nameof(RemotingHelper.CleanupWorkspace)); + #endregion + + #region Close + public void Close() + { + if (!IsRunning) + { + return; + } + Cleanup(); + + CloseRemotingService(); + CloseHostProcess(); + } + + private void CloseHostProcess() { - IntegrationHelper.RetryDteCall(() => _dte.Quit()); + IntegrationHelper.RetryRpcCall(() => _dte.Quit()); IntegrationHelper.KillProcess(_hostProcess); } - private void CleanupRemotingService() + private void CloseRemotingService() { try { - if ((IntegrationHelper.RetryDteCall(() => _dte?.Commands.Item(VisualStudioCommandNames.VsStopServiceCommand).IsAvailable).GetValueOrDefault())) + if ((IntegrationHelper.RetryRpcCall(() => _dte?.Commands.Item(VisualStudioCommandNames.VsStopServiceCommand).IsAvailable).GetValueOrDefault())) { - ExecuteDteCommandAsync(VisualStudioCommandNames.VsStopServiceCommand).GetAwaiter().GetResult(); + _dte.ExecuteCommandAsync(VisualStudioCommandNames.VsStopServiceCommand).GetAwaiter().GetResult(); } } finally { - if (_serviceChannel != null) + if (_integrationServiceChannel != null) { - ChannelServices.UnregisterChannel(_serviceChannel); + ChannelServices.UnregisterChannel(_integrationServiceChannel); } } } + #endregion } } diff --git a/src/VisualStudio/TestUtilities/VisualStudioInstanceContext.cs b/src/VisualStudio/TestUtilities/VisualStudioInstanceContext.cs index e877232628e5c..9609a63e06859 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioInstanceContext.cs +++ b/src/VisualStudio/TestUtilities/VisualStudioInstanceContext.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; namespace Roslyn.VisualStudio.Test.Utilities { @@ -28,7 +26,7 @@ public void Dispose() { try { - _instance.CloseAndDeleteOpenSolution(); + _instance.Cleanup(); _instanceFactory.NotifyCurrentInstanceContextDisposed(canReuse: true); } catch (Exception) diff --git a/src/VisualStudio/TestUtilities/VisualStudioInstanceFactory.cs b/src/VisualStudio/TestUtilities/VisualStudioInstanceFactory.cs index 909e79e925ccd..29bcdbfcaa143 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioInstanceFactory.cs +++ b/src/VisualStudio/TestUtilities/VisualStudioInstanceFactory.cs @@ -11,7 +11,6 @@ public sealed class VisualStudioInstanceFactory : IDisposable { internal static readonly string VsProductVersion = Settings.Default.VsProductVersion; - internal static readonly string VsProgId = $"VisualStudio.DTE.{VsProductVersion}"; internal static readonly string Wow6432Registry = Environment.Is64BitProcess ? "WOW6432Node" : string.Empty; @@ -78,10 +77,7 @@ private void StartNewInstance() private static Process StartNewVisualStudioProcess() { - // TODO: This might not be needed anymore as I don't believe we do things which risk corrupting the MEF cache. However, - // it is still useful to do in case some other action corruped the MEF cache as we don't have to restart the host - Process.Start(VsExeFile, $"/clearcache {VsLaunchArgs}").WaitForExit(); - Process.Start(VsExeFile, $"/updateconfiguration {VsLaunchArgs}").WaitForExit(); + Process.Start(VsExeFile, $"/resetsettings General.vssettings /command \"File.Exit\" {VsLaunchArgs}").WaitForExit(); // Make sure we kill any leftover processes spawned by the host IntegrationHelper.KillProcess("DbgCLR"); diff --git a/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj b/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj index 8785e1897ea25..d6c5ce41efdac 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj +++ b/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj @@ -18,14 +18,19 @@ AnyCPU + + + + + + - True True @@ -33,13 +38,15 @@ + - + + @@ -64,20 +71,33 @@ + + + {1ee8cad3-55f9-4d91-96b2-084641da9a6c} + CodeAnalysis + {8da861d8-0cce-4334-b6c0-01a846c881ec} VisualStudio + + {3cdeeab7-2256-418a-beb2-620b5cb16302} + EditorFeatures + {76c6f005-c89d-4348-bb4a-39189ddbeb52} ServicesTestUtilities + + {edc68a0e-c68d-4a74-91b7-bf38ec909888} + Features + {01e9bd68-0339-4a13-b42f-a3ca84d164f3} InteractiveWindow @@ -86,6 +106,18 @@ {20bb6fac-44d2-4d76-abfe-0c1e163a1a4f} VisualStudioInteractiveWindow + + {e2e889a5-2489-4546-9194-47c63e49eaeb} + Diagnostics + + + {5f8d2414-064a-4b3a-9b42-8e2a04246be5} + Workspaces + + + {86fd5b9a-4fa0-4b10-b59f-cfaf077a859c} + ServicesVisualStudio + {737ff62c-f068-4317-84d0-bfb210c17a4e} CSharpVisualStudioRepl diff --git a/src/VisualStudio/TestUtilities/Window/CSharpInteractiveWindow.cs b/src/VisualStudio/TestUtilities/Window/CSharpInteractiveWindow.cs new file mode 100644 index 0000000000000..0fe871a39e4b8 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Window/CSharpInteractiveWindow.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Roslyn.VisualStudio.Test.Utilities.Remoting; + +namespace Roslyn.VisualStudio.Test.Utilities +{ + public class CSharpInteractiveWindow : InteractiveWindow + { + internal const string CreateMethodName = nameof(InteractiveWindowWrapper.CreateForCSharp); + internal const string DteViewCommand = "View.C#Interactive"; + internal const string DteWindowTitle = "C# Interactive"; + + public CSharpInteractiveWindow(VisualStudioInstance visualStudioInstance) : base(visualStudioInstance, DteViewCommand, DteWindowTitle, CreateMethodName) + { + } + } +} diff --git a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs index 21aceee010cf2..93df5d7b8e881 100644 --- a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs @@ -1,5 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Reflection; +using System.Threading.Tasks; +using EnvDTE; +using Roslyn.VisualStudio.Test.Utilities.Interop; using Roslyn.VisualStudio.Test.Utilities.Remoting; namespace Roslyn.VisualStudio.Test.Utilities @@ -7,24 +12,295 @@ namespace Roslyn.VisualStudio.Test.Utilities /// Provides a means of interacting with the active editor window in the Visual Studio host. public class EditorWindow { - private readonly VisualStudioInstance _visualStudio; + public const char ENTER = '\u000D'; + public const char TAB = '\u0009'; + public const char ESC = '\u001B'; + public const char ESCAPE = '\u001B'; + public const char HOME = '\u0024'; + public const char END = '\u0023'; + public const char LEFT = '\u0025'; + public const char RIGHT = '\u0027'; + public const char UP = '\u0026'; + public const char DOWN = '\u0028'; + public const char PGUP = '\u0021'; + public const char PGDN = '\u0022'; + public const char NUMLOCK = '\u0090'; + public const char SCROLLLOCK = '\u0091'; + public const char PRTSC = '\u002C'; + public const char BREAK = '\u0003'; + public const char BACKSPACE = '\u0008'; + public const char BKSP = '\u0008'; + public const char BS = '\u0008'; + public const char CLEAR = '\u000C'; + public const char CAPSLOCK = '\u0014'; + public const char INSERT = '\u002D'; + public const char DEL = '\u002E'; + public const char DELETE = '\u002E'; + public const char HELP = '\u002F'; + public const char F1 = '\u0070'; + public const char F2 = '\u0071'; + public const char F3 = '\u0072'; + public const char F4 = '\u0073'; + public const char F5 = '\u0074'; + public const char F6 = '\u0075'; + public const char F7 = '\u0076'; + public const char F8 = '\u0077'; + public const char F9 = '\u0078'; + public const char F10 = '\u0079'; + public const char F11 = '\u007A'; + public const char F12 = '\u007B'; + public const char F13 = '\u007C'; + public const char F14 = '\u007D'; + public const char F15 = '\u007E'; + public const char F16 = '\u007F'; + public const char MULTIPLY = '\u006A'; + public const char ADD = '\u006B'; + public const char SUBTRACT = '\u006D'; + public const char DIVIDE = '\u006F'; - internal EditorWindow(VisualStudioInstance visualStudio) + private readonly VisualStudioInstance _visualStudioInstance; + private readonly EditorWindowWrapper _editorWindowWrapper; + + internal EditorWindow(VisualStudioInstance visualStudioInstance) + { + _visualStudioInstance = visualStudioInstance; + + var integrationService = _visualStudioInstance.IntegrationService; + _editorWindowWrapper = integrationService.Execute(typeof(EditorWindowWrapper), nameof(EditorWindowWrapper.Create), (BindingFlags.Public | BindingFlags.Static)); + } + + public string CurrentLineTextBeforeCursor + { + get + { + // Clear the current selected text, if any, so it is not included in the returned value + if (!string.IsNullOrEmpty(TextSelection.Text)) + { + TextSelection.CharLeft(Extend: false, Count: 1); + } + + // Select everything from the cursor to the beginning of the line + TextSelection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn, Extend: true); + + var currentLineTextBeforeCursor = TextSelection.Text; + + // Restore the cursor to its previous position + if (currentLineTextBeforeCursor != string.Empty) + { + TextSelection.CharRight(Extend: false, Count: 1); + } + + return currentLineTextBeforeCursor; + } + } + + public string CurrentLineTextAfterCursor { - _visualStudio = visualStudio; + get + { + // Clear the current selected text, if any, so it is not included in the returned value + if (!string.IsNullOrEmpty(TextSelection.Text)) + { + TextSelection.CharRight(Extend: false, Count: 1); + } + + // Select everything from the cursor to the end of the line + TextSelection.EndOfLine(Extend: true); + + var currentLineTextAfterCursor = TextSelection.Text; + + // Restore the cursor to its previous position + if (currentLineTextAfterCursor != string.Empty) + { + TextSelection.CharLeft(Extend: false, Count: 1); + } + + return currentLineTextAfterCursor; + } } public string Text { get { - return _visualStudio.ExecuteOnHostProcess(typeof(RemotingHelper), nameof(RemotingHelper.GetActiveTextViewContents), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + return _editorWindowWrapper.Contents; } set { - _visualStudio.ExecuteOnHostProcess(typeof(RemotingHelper), nameof(RemotingHelper.SetActiveTextViewContents), System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static, value); + _editorWindowWrapper.Contents = value; + } + } + + private TextSelection TextSelection => (TextSelection)(IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.ActiveDocument.Selection)); + + public void Activate() => IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.ActiveDocument.Activate()); + + public void ClearTextSelection() => TextSelection?.Cancel(); + + public void Find(string expectedText) + { + Activate(); + ClearTextSelection(); + + var dteFind = IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.Find); + + dteFind.Action = vsFindAction.vsFindActionFind; + dteFind.FindWhat = expectedText; + dteFind.MatchCase = true; + dteFind.MatchInHiddenText = true; + dteFind.MatchWholeWord = true; + dteFind.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral; + dteFind.Target = vsFindTarget.vsFindTargetCurrentDocument; + + var findResult = IntegrationHelper.RetryRpcCall(() => dteFind.Execute()); + + if (findResult != vsFindResult.vsFindResultFound) + { + throw new Exception($"The specified text was not found. ExpectedText: '{expectedText}'; ActualText: '{Text}'"); } } + + public void PlaceCursor(string marker) => Find(marker); + + public Task TypeTextAsync(string text, int wordsPerMinute = 120) + { + Activate(); + return SendKeysAsync(text.Replace("\r\n", "\r").Replace("\n", "\r"), wordsPerMinute); + } + + private async Task SendKeysAsync(string text, int wordsPerMinute) + { + var charactersPerSecond = (wordsPerMinute * 4.5) / 60; + var delayBetweenCharacters = (int)((1 / charactersPerSecond) * 1000); + + foreach (var character in text) + { + var foregroundWindow = IntPtr.Zero; + var inputBlocked = false; + + try + { + inputBlocked = IntegrationHelper.BlockInput(); + foregroundWindow = IntegrationHelper.GetForegroundWindow(); + + _visualStudioInstance.IntegrationService.Execute(typeof(RemotingHelper), nameof(RemotingHelper.ActivateMainWindow)); + + var vk = User32.VkKeyScan(character); + + if (vk == -1) + { + SendCharacter(character); + } + else + { + if ((vk & 0x0100) != 0) // SHIFT + { + SendKey(User32.VK_SHIFT, User32.KEYEVENTF_NONE); + } + + if ((vk & 0x0200) != 0) // CTRL + { + SendKey(User32.VK_CONTROL, User32.KEYEVENTF_NONE); + } + + if ((vk & 0x0400) != 0) // ALT + { + SendKey(User32.VK_MENU, User32.KEYEVENTF_NONE); + } + + SendKey((ushort)(vk & 0xFF)); + + if ((vk & 0x0100) != 0) // SHIFT + { + SendKey(User32.VK_SHIFT, User32.KEYEVENTF_KEYUP); + } + + if ((vk & 0x0200) != 0) // CTRL + { + SendKey(User32.VK_CONTROL, User32.KEYEVENTF_KEYUP); + } + + if ((vk & 0x0400) != 0) // ALT + { + SendKey(User32.VK_MENU, User32.KEYEVENTF_KEYUP); + } + } + } + finally + { + if (foregroundWindow != IntPtr.Zero) + { + IntegrationHelper.SetForegroundWindow(foregroundWindow); + } + + if (inputBlocked) + { + IntegrationHelper.UnblockInput(); + } + } + + await Task.Delay(delayBetweenCharacters); + } + } + + private bool IsExtendedKey(ushort vk) => ((vk >= User32.VK_PRIOR) && (vk <= User32.VK_DOWN)) || (vk == User32.VK_INSERT) || (vk == User32.VK_DELETE); + + private void SendKey(ushort vk) + { + SendKey(vk, User32.KEYEVENTF_NONE); + SendKey(vk, User32.KEYEVENTF_KEYUP); + } + + private void SendKey(ushort vk, uint dwFlags) + { + var input = new User32.INPUT[] { + new User32.INPUT() { + Type = User32.INPUT_KEYBOARD, + ki = new User32.KEYBDINPUT() { + wVk = vk, + wScan = 0, + dwFlags = dwFlags, + time = 0, + dwExtraInfo = IntPtr.Zero + } + } + }; + + if (IsExtendedKey(vk)) + { + input[0].ki.dwFlags |= User32.KEYEVENTF_EXTENDEDKEY; + } + + IntegrationHelper.SendInput(input); + } + + private void SendCharacter(char character) + { + var input = new User32.INPUT[] { + new User32.INPUT() { + Type = User32.INPUT_KEYBOARD, + ki = new User32.KEYBDINPUT() { + wVk = 0, + wScan = character, + dwFlags = User32.KEYEVENTF_UNICODE, + time = 0, + dwExtraInfo = IntPtr.Zero + } + }, + new User32.INPUT() { + Type = User32.INPUT_KEYBOARD, + ki = new User32.KEYBDINPUT() { + wVk = 0, + wScan = character, + dwFlags = User32.KEYEVENTF_UNICODE | User32.KEYEVENTF_KEYUP, + time = 0, + dwExtraInfo = IntPtr.Zero + } + } + }; + + IntegrationHelper.SendInput(input); + } } } diff --git a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs index e2dec2a1d565e..dc3c888c5353c 100644 --- a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs @@ -1,43 +1,36 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Reflection; using System.Threading.Tasks; using Roslyn.VisualStudio.Test.Utilities.Remoting; namespace Roslyn.VisualStudio.Test.Utilities { /// Provides a means of interacting with the interactive window in the Visual Studio host. - public class InteractiveWindow + public abstract class InteractiveWindow { - private const string DteCSharpViewCommand = "View.C#Interactive"; - private const string DteCSharpWindowTitle = "C# Interactive"; private const string DteReplResetCommand = "InteractiveConsole.Reset"; - private const string ReplPromptText = "> "; private const string ReplSubmissionText = ". "; private readonly string _dteViewCommand; - private readonly VisualStudioInstance _host; + private readonly VisualStudioInstance _visualStudioInstance; private readonly InteractiveWindowWrapper _interactiveWindowWrapper; - /// Creates a instance that can interact with the C# interactive window in the Visual Studio host. - internal static InteractiveWindow CreateCSharpInteractiveWindow(VisualStudioInstance host) - => new InteractiveWindow(host, DteCSharpViewCommand, DteCSharpWindowTitle); - - internal InteractiveWindow(VisualStudioInstance host, string dteViewCommand, string dteWindowTitle) + internal InteractiveWindow(VisualStudioInstance visualStudioInstance, string dteViewCommand, string dteWindowTitle, string createMethodName) { - _host = host; + _visualStudioInstance = visualStudioInstance; _dteViewCommand = dteViewCommand; // We have to show the window at least once to ensure the interactive service is loaded. ShowAsync(waitForPrompt: false).GetAwaiter().GetResult(); - var dteWindow = _host.LocateDteWindowAsync(dteWindowTitle).GetAwaiter().GetResult(); - dteWindow.Close(); + var dteWindow = IntegrationHelper.WaitForNotNullAsync(() => _visualStudioInstance.Dte.LocateWindow(dteWindowTitle)).GetAwaiter().GetResult(); + IntegrationHelper.RetryRpcCall(() => dteWindow.Close()); // Return a wrapper to the actual interactive window service that exists in the host process - _interactiveWindowWrapper = _host.ExecuteOnHostProcess(typeof(RemotingHelper), nameof(RemotingHelper.CreateCSharpInteractiveWindowWrapper), (BindingFlags.Public | BindingFlags.Static)); + var integrationService = _visualStudioInstance.IntegrationService; + _interactiveWindowWrapper = integrationService.Execute(typeof(InteractiveWindowWrapper), createMethodName); } /// Gets the last output from the REPL. @@ -100,7 +93,7 @@ public string ReplTextWithoutPrompt public async Task ResetAsync(bool waitForPrompt = true) { - await _host.ExecuteDteCommandAsync(DteReplResetCommand).ConfigureAwait(continueOnCapturedContext: false); + await _visualStudioInstance.Dte.ExecuteCommandAsync(DteReplResetCommand).ConfigureAwait(continueOnCapturedContext: false); if (waitForPrompt) { @@ -110,7 +103,7 @@ public async Task ResetAsync(bool waitForPrompt = true) public async Task ShowAsync(bool waitForPrompt = true) { - await _host.ExecuteDteCommandAsync(_dteViewCommand).ConfigureAwait(continueOnCapturedContext: false); + await _visualStudioInstance.Dte.ExecuteCommandAsync(_dteViewCommand).ConfigureAwait(continueOnCapturedContext: false); if (waitForPrompt) { diff --git a/src/VisualStudio/TestUtilities/Workspace/SolutionExplorer.cs b/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs similarity index 60% rename from src/VisualStudio/TestUtilities/Workspace/SolutionExplorer.cs rename to src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs index 424a60d296e16..290308efc63ff 100644 --- a/src/VisualStudio/TestUtilities/Workspace/SolutionExplorer.cs +++ b/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs @@ -18,29 +18,27 @@ internal SolutionExplorer(VisualStudioInstance visualStudio) } /// Gets the solution currently loaded in the host process or null if no solution is currently loaded. - public Solution Solution - => _solution; + public Solution Solution => _solution; - /// Creates and loads a new solution in the host process, closing the existing solution without saving if one exists. - public Solution CreateSolution(string solutionName) + /// Creates and loads a new solution in the host process, optionally saving the existing solution if one exists. + public Solution CreateSolution(string solutionName, bool saveExistingSolutionIfExists = false) { - var dteSolution = _visualStudio.Dte.Solution; + var dteSolution = IntegrationHelper.RetryRpcCall(() => _visualStudio.Dte.Solution); - if (dteSolution.IsOpen) + if (IntegrationHelper.RetryRpcCall(() => dteSolution.IsOpen)) { - CloseSolution(saveFirst: false); + CloseSolution(saveExistingSolutionIfExists); } var solutionPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - dteSolution.Create(solutionPath, solutionName); + IntegrationHelper.DeleteDirectoryRecursively(solutionPath); + IntegrationHelper.RetryRpcCall(() => dteSolution.Create(solutionPath, solutionName)); _solution = new Solution((Solution2)(dteSolution), Path.Combine(solutionPath, $"{solutionName}.sln")); + return _solution; } - public void CloseSolution(bool saveFirst = false) - { - _visualStudio.Dte.Solution.Close(saveFirst); - } + public void CloseSolution(bool saveFirst = false) => IntegrationHelper.RetryRpcCall(() => _visualStudio.Dte.Solution.Close(saveFirst)); } } diff --git a/src/VisualStudio/TestUtilities/Workspace/Project.cs b/src/VisualStudio/TestUtilities/Workspace/Project.cs index 86859fa2b7cd4..df4d3878c0ce9 100644 --- a/src/VisualStudio/TestUtilities/Workspace/Project.cs +++ b/src/VisualStudio/TestUtilities/Workspace/Project.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; + using DteProject = EnvDTE.Project; namespace Roslyn.VisualStudio.Test.Utilities @@ -13,18 +15,25 @@ public class Project internal Project(DteProject dteProject, Solution solution, ProjectLanguage language) { + if (dteProject == null) + { + throw new ArgumentNullException(nameof(dteProject)); + } + + if (solution == null) + { + throw new ArgumentNullException(nameof(solution)); + } + _dteProject = dteProject; _solution = solution; _language = language; } - public DteProject DteProject - => _dteProject; + public DteProject DteProject => _dteProject; - public ProjectLanguage Language - => _language; + public ProjectLanguage Language => _language; - public Solution Solution - => _solution; + public Solution Solution => _solution; } } diff --git a/src/VisualStudio/TestUtilities/Workspace/Solution.cs b/src/VisualStudio/TestUtilities/Workspace/Solution.cs index 2aae0afbaa5a5..5cabfef88a9e8 100644 --- a/src/VisualStudio/TestUtilities/Workspace/Solution.cs +++ b/src/VisualStudio/TestUtilities/Workspace/Solution.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.IO; +using DteProject = EnvDTE.Project; using DteSolution = EnvDTE80.Solution2; namespace Roslyn.VisualStudio.Test.Utilities @@ -10,53 +12,86 @@ namespace Roslyn.VisualStudio.Test.Utilities /// Provides a means of interacting with the current solution loaded by the host process. public class Solution { - private static readonly IDictionary ProjectTemplateName = new Dictionary { - [ProjectTemplate.ClassLibrary] = "ClassLibrary.zip", - [ProjectTemplate.ConsoleApplication] = "ConsoleApplication.zip", - [ProjectTemplate.Website] = "EmptyWeb.zip", - [ProjectTemplate.WinFormsApplication] = "WindowsApplication.zip", - [ProjectTemplate.WpfApplication] = "WpfApplication.zip", - [ProjectTemplate.WebApplication] = "WebApplicationProject40" - }; - - private static readonly IDictionary ProjectLanguageName = new Dictionary { + private static readonly IDictionary ProjectLanguages = new Dictionary { [ProjectLanguage.CSharp] = "CSharp", [ProjectLanguage.VisualBasic] = "VisualBasic" }; private readonly DteSolution _dteSolution; - private readonly string _fileName; // Cache the filename as `_dteSolution` won't expose it unless the solution has been saved + private readonly string _fileName; // Cache the filename because `_dteSolution` won't expose it unless the solution has been saved + private readonly IDictionary _projectTemplates; internal Solution(DteSolution dteSolution, string fileName) { + if (dteSolution == null) + { + throw new ArgumentNullException(nameof(dteSolution)); + } + + if (string.IsNullOrWhiteSpace(fileName)) + { + throw new ArgumentNullException(nameof(fileName)); + } + _dteSolution = dteSolution; _fileName = fileName; + + _projectTemplates = new Dictionary { + [ProjectTemplate.ClassLibrary] = $@"Windows\{dteSolution.DTE.LocaleID}\ClassLibrary.zip", + [ProjectTemplate.ConsoleApplication] = "ConsoleApplication.zip", + [ProjectTemplate.Website] = "EmptyWeb.zip", + [ProjectTemplate.WinFormsApplication] = "WindowsApplication.zip", + [ProjectTemplate.WpfApplication] = "WpfApplication.zip", + [ProjectTemplate.WebApplication] = "WebApplicationProject40" + }; } - public DteSolution DteSolution - => _dteSolution; + public string DirectoryName => Path.GetDirectoryName(FileName); + + public DteSolution DteSolution => _dteSolution; public string FileName { get { - var solutionFullName = _dteSolution.FullName; + var solutionFullName = IntegrationHelper.RetryRpcCall(() => _dteSolution.FullName); return string.IsNullOrEmpty(solutionFullName) ? _fileName : solutionFullName; } } public Project AddProject(string projectName, ProjectTemplate projectTemplate, ProjectLanguage projectLanguage) { - var solutionFolder = Path.GetDirectoryName(FileName); - var projectPath = Path.Combine(solutionFolder, projectName); + var projectPath = Path.Combine(DirectoryName, projectName); var projectTemplatePath = GetProjectTemplatePath(projectTemplate, projectLanguage); - var dteProject = _dteSolution.AddFromTemplate(projectTemplatePath, projectPath, projectName, Exclusive: false); + var dteProject = IntegrationHelper.RetryRpcCall(() => _dteSolution.AddFromTemplate(projectTemplatePath, projectPath, projectName, Exclusive: false)); + + if (dteProject == null) + { + var dteSolutionProjects = IntegrationHelper.RetryRpcCall(() => _dteSolution.Projects); + + foreach (DteProject project in dteSolutionProjects) + { + var dteProjectName = IntegrationHelper.RetryRpcCall(() => project.Name); + + if (dteProjectName == projectName) + { + dteProject = project; + } + } + } + return new Project(dteProject, this, projectLanguage); } + public void Save() + { + Directory.CreateDirectory(DirectoryName); + IntegrationHelper.RetryRpcCall(() => _dteSolution.SaveAs(FileName)); + } + // TODO: Adjust language name based on whether we are using a web template private string GetProjectTemplatePath(ProjectTemplate projectTemplate, ProjectLanguage projectLanguage) - => _dteSolution.GetProjectTemplate(ProjectTemplateName[projectTemplate], ProjectLanguageName[projectLanguage]); + => IntegrationHelper.RetryRpcCall(() => _dteSolution.GetProjectTemplate(_projectTemplates[projectTemplate], ProjectLanguages[projectLanguage])); } } diff --git a/src/VisualStudio/TestUtilities/Workspace/Workspace.cs b/src/VisualStudio/TestUtilities/Workspace/Workspace.cs new file mode 100644 index 0000000000000..fb324bb021f15 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Workspace/Workspace.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using Roslyn.VisualStudio.Test.Utilities.Remoting; + +namespace Roslyn.VisualStudio.Test.Utilities +{ + public class Workspace + { + private readonly VisualStudioInstance _visualStudioInstance; + private readonly WorkspaceWrapper _workspaceWrapper; + + internal Workspace(VisualStudioInstance visualStudioInstance) + { + _visualStudioInstance = visualStudioInstance; + + var integrationService = _visualStudioInstance.IntegrationService; + _workspaceWrapper = integrationService.Execute(typeof(WorkspaceWrapper), nameof(WorkspaceWrapper.Create)); + } + + public bool UseSuggestionMode + { + get + { + return _workspaceWrapper.UseSuggestionMode; + } + + set + { + _workspaceWrapper.UseSuggestionMode = value; + } + } + } +} diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj index 658843418208e..cf977b0180e08 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj +++ b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj @@ -189,7 +189,6 @@ false - @@ -240,4 +239,4 @@ - \ No newline at end of file + diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index a7572892445d3..4f182b14d43f4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -6,26 +6,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { - private static SimplePool> s_definitionMapPool = - new SimplePool>(() => new MultiDictionary()); - - private static MultiDictionary AllocateDefinitionMap() - { - return s_definitionMapPool.Allocate(); - } - - private static void FreeDefinitionMap(MultiDictionary definitionMap) - { - definitionMap.Clear(); - s_definitionMapPool.Free(definitionMap); - } - /// /// this gives you SymbolTreeInfo for a metadata /// @@ -117,7 +104,7 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List unsortedNodes) { - var definitionMap = AllocateDefinitionMap(); + var definitionMap = OrderPreservingMultiDictionary.GetInstance(); try { LookupMetadataDefinitions(reader, globalNamespace, definitionMap); @@ -132,7 +119,7 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List.ValueSet definitionsWithSameName, + OrderPreservingMultiDictionary.ValueSet definitionsWithSameName, List unsortedNodes) { var node = new Node(name, parentIndex); @@ -148,7 +135,7 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List.GetInstance(); try { foreach (var definition in definitionsWithSameName) @@ -166,13 +153,13 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List definitionMap) + OrderPreservingMultiDictionary definitionMap) { switch (definition.Kind) { @@ -187,7 +174,7 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List definitionMap) + OrderPreservingMultiDictionary definitionMap) { // Only bother looking for extension methods in static types. if ((typeDefinition.Attributes & TypeAttributes.Abstract) != 0 && @@ -235,7 +222,7 @@ private static void GenerateMetadataNodes(MetadataReader metadataReader, List definitionMap) + OrderPreservingMultiDictionary definitionMap) { foreach (var child in namespaceDefinition.NamespaceDefinitions) { diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index ad3eecdf0c2d0..880ce32443c74 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -41,6 +41,9 @@ InternalUtilities\ImmutableArrayExtensions.cs + + Utilities\CompilerUtilities\OrderPreservingMultiDictionary.cs + Collections\PooledStringBuilder.cs @@ -280,6 +283,7 @@ + diff --git a/src/Workspaces/CoreTest/ServicesTest.csproj b/src/Workspaces/CoreTest/ServicesTest.csproj index 3db534bcb7513..7d1d4c535fe33 100644 --- a/src/Workspaces/CoreTest/ServicesTest.csproj +++ b/src/Workspaces/CoreTest/ServicesTest.csproj @@ -13,7 +13,9 @@ Microsoft.CodeAnalysis.UnitTests Roslyn.Services.UnitTests v4.6 + true + $(DefineConstants);MSBUILD12 diff --git a/src/Workspaces/CoreTest/app.config b/src/Workspaces/CoreTest/app.config index a5e030b1e06b6..5c69694d20f0f 100644 --- a/src/Workspaces/CoreTest/app.config +++ b/src/Workspaces/CoreTest/app.config @@ -13,36 +13,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -