From 4578f8222b5ec36357dd67503ed537d055bb3f17 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 11 May 2016 12:50:30 -0700 Subject: [PATCH 01/28] Improve OrderPreservingMultiDictionary. 1. Support allocation-free enumeration of the dictionary. 2. Pool internal data so that less garbage is churned when creating and releasing dictionaries. --- .../OrderPreservingMultiDictionary.cs | 269 +++++++++++------- 1 file changed, 169 insertions(+), 100 deletions(-) diff --git a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs index a7f32443622e9..52be50b18b713 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; @@ -15,7 +17,8 @@ namespace Microsoft.CodeAnalysis.Collections /// /// Always uses the default comparer. /// - internal sealed class OrderPreservingMultiDictionary + internal sealed class OrderPreservingMultiDictionary : + IEnumerable.ValueSet>> { #region Pooling @@ -30,6 +33,11 @@ public void Free() { if (_dictionary != null) { + foreach (var kvp in _dictionary) + { + kvp.Value.Free(); + } + _dictionary.Free(); _dictionary = null; } @@ -57,159 +65,220 @@ public void Free() #endregion Pooling + private static readonly Dictionary s_emptyDictionary = new Dictionary(); + + 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; + ValueSet item; if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) { - 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); - } + _dictionary[k] = item.Add(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)) + { + 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 + // 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 readonly object _value; + private static ObjectPool> s_builderPool = new ObjectPool>( + () => new ArrayBuilder(size: 2)); + + 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; + if (arrayBuilder != null) + { + arrayBuilder.Clear(); + s_builderPool.Free(arrayBuilder); + } + } + + internal V this[int index] + { + get { - var arrayBuilder = item as ArrayBuilder; + 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 + { + 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 Add(V item) + { + var arrayBuilder = _value as ArrayBuilder; + if (arrayBuilder == null) + { + // Promote from singleton V to ArrayBuilder. + Debug.Assert(_value is V, "_value must be a V"); + arrayBuilder = s_builderPool.Allocate(); + 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; + _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 From 0863ef985d2bfa1548273e0e22ba2f3009e055c8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 11 May 2016 13:02:24 -0700 Subject: [PATCH 02/28] Move to using a multidictionary in our SymbolTreeInfo code. --- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 31 ++++++------------- .../Core/Portable/Workspaces.csproj | 3 ++ 2 files changed, 12 insertions(+), 22 deletions(-) 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 c81ead23270a7..9d0f61ec65b76 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 From 253b5f8d2d2b8084291e45ea64782ffb4131d11b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 11 May 2016 13:10:45 -0700 Subject: [PATCH 03/28] Clean up code and add comments to clarify. --- .../OrderPreservingMultiDictionary.cs | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs index 52be50b18b713..f73f6695a13f5 100644 --- a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs +++ b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs @@ -10,9 +10,8 @@ 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. @@ -33,6 +32,7 @@ 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(); @@ -65,8 +65,11 @@ 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() @@ -78,17 +81,20 @@ private void EnsureDictionary() _dictionary = _dictionary ?? PooledDictionary.GetInstance(); } - public bool IsEmpty => _dictionary == null; + public bool IsEmpty => _dictionary == null; /// /// Add a value to the dictionary. /// public void Add(K k, V v) { - ValueSet item; - if (!this.IsEmpty && _dictionary.TryGetValue(k, out item)) + ValueSet valueSet; + if (!this.IsEmpty && _dictionary.TryGetValue(k, out valueSet)) { - _dictionary[k] = item.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 { @@ -123,6 +129,7 @@ IEnumerator IEnumerable.GetEnumerator() ValueSet valueSet; if (!this.IsEmpty && _dictionary.TryGetValue(k, out valueSet)) { + Debug.Assert(valueSet.Count >= 1); return valueSet.Items; } @@ -140,16 +147,19 @@ IEnumerator IEnumerable.GetEnumerator() public struct ValueSet : IEnumerable { - // 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. + /// Never null. /// private readonly object _value; + + // By default we allocate array builders with a size of two. That's two 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. private static ObjectPool> s_builderPool = new ObjectPool>( () => new ArrayBuilder(size: 2)); - + internal ValueSet(V value) { _value = value; @@ -174,6 +184,8 @@ internal void Free() { get { + Debug.Assert(this.Count >= 1); + var arrayBuilder = _value as ArrayBuilder; if (arrayBuilder == null) { @@ -197,6 +209,8 @@ internal ImmutableArray Items { get { + Debug.Assert(this.Count >= 1); + var arrayBuilder = _value as ArrayBuilder; if (arrayBuilder == null) { @@ -228,8 +242,10 @@ public Enumerator GetEnumerator() return new Enumerator(this); } - internal ValueSet Add(V item) + internal ValueSet WithAddedItem(V item) { + Debug.Assert(this.Count >= 1); + var arrayBuilder = _value as ArrayBuilder; if (arrayBuilder == null) { @@ -257,6 +273,7 @@ public Enumerator(ValueSet valueSet) { _valueSet = valueSet; _count = _valueSet.Count; + Debug.Assert(_count >= 1); _index = -1; } From de0888c13cbf54b0494ef3a5e98759536dfed3bd Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 11 May 2016 14:29:43 -0700 Subject: [PATCH 04/28] Use the built-in pooling support already in ArrayBuilder. --- .../OrderPreservingMultiDictionary.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs index f73f6695a13f5..2c2a10a979a3b 100644 --- a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs +++ b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs @@ -153,13 +153,6 @@ public struct ValueSet : IEnumerable /// private readonly object _value; - // By default we allocate array builders with a size of two. That's two 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. - private static ObjectPool> s_builderPool = new ObjectPool>( - () => new ArrayBuilder(size: 2)); - internal ValueSet(V value) { _value = value; @@ -175,8 +168,7 @@ internal void Free() var arrayBuilder = _value as ArrayBuilder; if (arrayBuilder != null) { - arrayBuilder.Clear(); - s_builderPool.Free(arrayBuilder); + arrayBuilder.Free(); } } @@ -251,7 +243,12 @@ internal ValueSet WithAddedItem(V item) { // Promote from singleton V to ArrayBuilder. Debug.Assert(_value is V, "_value must be a V"); - arrayBuilder = s_builderPool.Allocate(); + + // 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); } From 20da7832b53bfdc732347f28f7be393092c86fec Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 11 May 2016 14:33:00 -0700 Subject: [PATCH 05/28] Simplify code. --- .../Portable/Collections/OrderPreservingMultiDictionary.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs index 2c2a10a979a3b..40c444b592a8f 100644 --- a/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs +++ b/src/Compilers/Core/Portable/Collections/OrderPreservingMultiDictionary.cs @@ -166,10 +166,7 @@ internal ValueSet(ArrayBuilder values) internal void Free() { var arrayBuilder = _value as ArrayBuilder; - if (arrayBuilder != null) - { - arrayBuilder.Free(); - } + arrayBuilder?.Free(); } internal V this[int index] From 67bde213edd5ef4abb1a6a67fcca9594b5256ae5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 10 May 2016 13:44:03 -0700 Subject: [PATCH 06/28] Extending the supported functionality of the VisualStudio IntegrationTest Utilities and adding the start of 'CSharpAutomaticBraceCompletion' --- src/EditorFeatures/Core/EditorFeatures.csproj | 1 + src/Features/Core/Portable/Features.csproj | 1 + .../CSharp/CSharpAutomaticBraceCompletion.cs | 203 ++++++++++++++ .../IntegrationTests/CSharp/CSharpBuild.cs | 3 +- .../CSharp/CSharpInteractiveDemo.cs | 3 +- .../SharedIntegrationHostFixture.cs | 2 +- .../VisualBasic/BasicBuild.cs | 3 +- .../VisualStudioIntegrationTests.csproj | 5 + .../TestUtilities/Extensions/DteExtensions.cs | 34 +++ .../IntegrationServiceExtensions.cs | 38 +++ .../TestUtilities/IntegrationHelper.cs | 5 + .../TestUtilities/IntegrationService.cs | 6 +- .../TestUtilities/Interop/User32.cs | 100 +++++++ .../Remoting/EditorWindowWrapper.cs | 28 ++ .../Remoting/InteractiveWindowWrapper.cs | 6 +- .../TestUtilities/Remoting/RemotingHelper.cs | 157 ++++++++--- .../Remoting/WorkspaceWrapper.cs | 55 ++++ .../TestUtilities/VisualStudioCommandNames.cs | 6 - .../TestUtilities/VisualStudioInstance.cs | 139 +++++----- .../VisualStudioInstanceContext.cs | 10 +- .../VisualStudioInstanceFactory.cs | 6 +- .../VisualStudioTestUtilities.csproj | 35 ++- .../Window/CSharpInteractiveWindow.cs | 17 ++ .../TestUtilities/Window/EditorWindow.cs | 247 +++++++++++++++++- .../TestUtilities/Window/InteractiveWindow.cs | 26 +- .../{Workspace => Window}/SolutionExplorer.cs | 18 +- .../TestUtilities/Workspace/Project.cs | 21 +- .../TestUtilities/Workspace/Solution.cs | 63 +++-- .../TestUtilities/Workspace/Workspace.cs | 34 +++ .../Core/Portable/Workspaces.csproj | 1 + 30 files changed, 1084 insertions(+), 189 deletions(-) create mode 100644 src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs create mode 100644 src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs create mode 100644 src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs create mode 100644 src/VisualStudio/TestUtilities/Remoting/EditorWindowWrapper.cs create mode 100644 src/VisualStudio/TestUtilities/Remoting/WorkspaceWrapper.cs create mode 100644 src/VisualStudio/TestUtilities/Window/CSharpInteractiveWindow.cs rename src/VisualStudio/TestUtilities/{Workspace => Window}/SolutionExplorer.cs (74%) create mode 100644 src/VisualStudio/TestUtilities/Workspace/Workspace.cs diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 53b25318ce0c2..24406245cf587 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -108,6 +108,7 @@ + diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 403842abc39b0..a40cf33bdcc1d 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -62,6 +62,7 @@ + diff --git a/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs new file mode 100644 index 0000000000000..b5fbbfae8f35d --- /dev/null +++ b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs @@ -0,0 +1,203 @@ +// 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("\t"); + + 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("\nvar 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("\nvar 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;\n"); + + 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\t"); + + 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("\u001B"); // 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/TestUtilities/Extensions/DteExtensions.cs b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs new file mode 100644 index 0000000000000..fb2279bcf4f99 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.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.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.RetryDteCall(() => dte.ExecuteCommand(command, args)); + } + + public static Task LocateWindowAsync(this DTE dte, string windowTitle) => IntegrationHelper.WaitForNotNullAsync(() => IntegrationHelper.RetryDteCall(() => + { + foreach (Window window in dte.Windows) + { + if (window.Caption.Equals(windowTitle)) + { + return window; + } + } + return null; + })); + + public static Task WaitForCommandAvailabilityAsync(this DTE dte, string command) + => IntegrationHelper.WaitForResultAsync(() => IntegrationHelper.RetryDteCall(() => 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..d29b6b12ee267 --- /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 objectUri = integrationService.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); + + if (!string.IsNullOrWhiteSpace(objectUri)) + { + 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 (string.IsNullOrWhiteSpace(objectUri)) + { + 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..24be9b2e7fd27 100644 --- a/src/VisualStudio/TestUtilities/IntegrationHelper.cs +++ b/src/VisualStudio/TestUtilities/IntegrationHelper.cs @@ -114,6 +114,11 @@ public static void KillProcess(string processName) } } + public static void SetFocus(IntPtr windowHandle) + { + User32.SetForegroundWindow(windowHandle); + } + /// Locates the DTE object for the specified process. public static DTE TryLocateDteForProcess(Process process) { 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/User32.cs b/src/VisualStudio/TestUtilities/Interop/User32.cs index a744b6e2c8b0c..0442f8dc2499c 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,79 @@ internal static class User32 public const uint GW_CHILD = 5; public const uint GW_ENABLEDPOPUP = 6; + public const uint INPUT_MOUSE = 0; + public const uint INPUT_KEYBOARD = 1; + public const uint INPUT_HARDWARE = 2; + + 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 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,6 +102,12 @@ internal static class User32 [In] IntPtr lParam ); + [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( @@ -43,6 +121,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 +136,13 @@ internal static class User32 [In] uint uCmd ); + [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 +158,16 @@ internal static class User32 [In] IntPtr wParam, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam ); + + [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 = "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..a2689ce68943f 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,130 @@ 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 T ExecuteOnUIThread(Func action) - => Application.Current.Dispatcher.Invoke(action); + private static IVsInteractiveWindow CSharpVsInteractiveWindow => InvokeOnUIThread(() => CSharpVsInteractiveWindowProvider.Open(0, true)); - private static IWpfTextViewHost GetActiveTextViewHost() - { - // 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(); + private static CSharpVsInteractiveWindowProvider CSharpVsInteractiveWindowProvider => ComponentModel.GetService(); + + private static Application CurrentApplication => Application.Current; + + private static Dispatcher CurrentApplicationDispatcher => CurrentApplication.Dispatcher; - var vsTextManager = (IVsTextManager)(ServiceProvider.GlobalProvider.GetService(typeof(SVsTextManager))); + private static ExportProvider DefaultComponentModelExportProvider => ComponentModel.DefaultExportProvider; - IVsTextView vsTextView = null; - var hresult = vsTextManager.GetActiveView(fMustHaveFocus: 1, pBuffer: null, ppView: out vsTextView); + public static DTE DTE => GetGlobalService(typeof(SDTE)); - if (hresult != VSConstants.S_OK) + 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 + { + get { - throw Marshal.GetExceptionForHR(hresult); + IVsTextView vsTextView = null; + + var hresult = VsTextManager.GetActiveView(fMustHaveFocus: 1, pBuffer: null, ppView: out vsTextView); + Marshal.ThrowExceptionForHR(hresult); + + return vsTextView; } + } - var vsUserData = (IVsUserData)(vsTextView); + private static TestingOnly_WaitingService WaitingService => DefaultComponentModelExportProvider.GetExport().Value; - 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); + } + + public static void CleanupWorkspace() + { + LoadRoslynPackage(); + VisualStudioWorkspace.TestHookPartialSolutionsDisabled = true; } - private static void WaitForApplicationIdle() - => Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); + 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..8aa8ca061a63a 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,38 +95,32 @@ 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); + #region Cleanup + public void Cleanup() + { + CleanupOpenSolution(); + CleanupInteractiveWindow(); + CleanupWaitingService(); + CleanupWorkspace(); + } - public void Close() + private void CleanupInteractiveWindow() { - if (!IsRunning) + foreach (Window window in Dte.Windows) { - return; + if (window.Caption == CSharpInteractiveWindow.DteWindowTitle) + { + window.Close(); + break; + } } - - CloseAndDeleteOpenSolution(); - CleanupRemotingService(); - CleanupHostProcess(); } - public void CloseAndDeleteOpenSolution() + private void CleanupOpenSolution() { - IntegrationHelper.RetryDteCall(() => _dte.Documents.CloseAll(EnvDTE.vsSaveChanges.vsSaveChangesNo)); + IntegrationHelper.RetryDteCall(() => _dte.Documents.CloseAll(vsSaveChanges.vsSaveChangesNo)); if (IntegrationHelper.RetryDteCall(() => _dte.Solution) != null) { @@ -170,29 +154,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.KillProcess(_hostProcess); } - private void CleanupRemotingService() + private void CloseRemotingService() { try { if ((IntegrationHelper.RetryDteCall(() => _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..00893384f9591 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj +++ b/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj @@ -18,14 +18,18 @@ AnyCPU + + + + + - True True @@ -33,13 +37,15 @@ + - + + @@ -64,20 +70,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 +105,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..e39952d359312 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,256 @@ 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; + private readonly VisualStudioInstance _visualStudioInstance; + private readonly EditorWindowWrapper _editorWindowWrapper; - internal EditorWindow(VisualStudioInstance visualStudio) + internal EditorWindow(VisualStudioInstance visualStudioInstance) { - _visualStudio = visualStudio; + _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 + { + 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)(_visualStudioInstance.Dte.ActiveDocument.Selection); + + public void Activate() => _visualStudioInstance.Dte.ActiveDocument.Activate(); + + public void ClearTextSelection() => TextSelection?.Cancel(); + + public void Find(string expectedText) + { + Activate(); + ClearTextSelection(); + + var dteFind = _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 = 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 foregroundWindowHandle = IntPtr.Zero; + var inputBlocked = false; + + try + { + inputBlocked = User32.BlockInput(true); + foregroundWindowHandle = User32.GetForegroundWindow(); + + var activeWindowHandle = (IntPtr)(_visualStudioInstance.Dte.ActiveWindow.HWnd); + + if (activeWindowHandle == IntPtr.Zero) + { + activeWindowHandle = (IntPtr)(_visualStudioInstance.Dte.MainWindow.HWnd); + } + + IntegrationHelper.SetFocus(activeWindowHandle); + + var vk = User32.VkKeyScan(character); + + if (vk == -1) + { + SendCharacter(character); + } + else + { + if ((vk & 0x0100) != 0) // SHIFT + { + SendKey(User32.VK_SHIFT); + } + + if ((vk & 0x0200) != 0) // CTRL + { + SendKey(User32.VK_CONTROL); + } + + if ((vk & 0x0400) != 0) // ALT + { + SendKey(User32.VK_MENU); + } + + 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 (foregroundWindowHandle != IntPtr.Zero) + { + IntegrationHelper.SetFocus(foregroundWindowHandle); + } + + if (inputBlocked) + { + User32.BlockInput(false); + } + } + + 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); + SendKey(vk, User32.KEYEVENTF_KEYUP); + } + + private void SendKey(ushort vk, uint dwFlags = 0) + { + var inputs = 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)) + { + inputs[0].ki.dwFlags |= User32.KEYEVENTF_EXTENDEDKEY; } + + User32.SendInput(1, inputs, User32.SizeOf_INPUT); + } + + private void SendCharacter(char character) + { + var inputs = 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 + } + } + }; + + User32.SendInput(2, inputs, User32.SizeOf_INPUT); } } } diff --git a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs index e2dec2a1d565e..4f2e1f95ff077 100644 --- a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs @@ -1,43 +1,37 @@ // 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(); + var dte = _visualStudioInstance.Dte; + var dteWindow = dte.LocateWindowAsync(dteWindowTitle).GetAwaiter().GetResult(); 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 +94,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 +104,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 74% rename from src/VisualStudio/TestUtilities/Workspace/SolutionExplorer.cs rename to src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs index 424a60d296e16..4a783f42531ca 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; if (dteSolution.IsOpen) { - CloseSolution(saveFirst: false); + CloseSolution(saveExistingSolutionIfExists); } var solutionPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - dteSolution.Create(solutionPath, solutionName); + IntegrationHelper.DeleteDirectoryRecursively(solutionPath); + 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) => _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..6ebd4ecabed4d 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,31 +12,43 @@ 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 { @@ -47,16 +61,33 @@ public string FileName 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); + + if (dteProject == null) + { + foreach (DteProject project in DteSolution.Projects) + { + if (project.Name == projectName) + { + dteProject = project; + } + } + } + return new Project(dteProject, this, projectLanguage); } + public void Save() + { + Directory.CreateDirectory(DirectoryName); + _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]); + => _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/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index c81ead23270a7..a21ccc5457feb 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -280,6 +280,7 @@ + From 4d1562e2d7fbe1f4df7bbbd758cd0df8824fa23e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 13 May 2016 00:14:07 +0800 Subject: [PATCH 07/28] Make the option supports persisting. --- .../CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs index 0e6767bb0a196..034970050dd5e 100644 --- a/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs +++ b/src/VisualStudio/CSharp/Impl/Options/CSharpSettingsManagerOptionSerializer.cs @@ -164,6 +164,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; From 75c1babbf2ce87ee64aa0f2a86e5e9bc726da3aa Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 13 May 2016 00:20:55 +0800 Subject: [PATCH 08/28] Add a property for the AutomationObject. --- src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs index 5fc0a872e33f1..6d91ab6c6b288 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject.cs @@ -31,6 +31,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); } From a4a5ffdc73ee0b212f8ece58c73277e41ec915ac Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 12 May 2016 13:11:07 -0700 Subject: [PATCH 09/28] Avoid loading the Elfie dll until the user actualy enables the search feature. --- .../SymbolSearchService.Update.cs | 27 +++++++++---------- .../Def/SymbolSearch/SymbolSearchService.cs | 18 ++++++------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs index e588990c77485..fd5dcc94d2b08 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); diff --git a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs index f2a30c81729e6..6a2f13e2f5dcc 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(); + // Value is typed as 'object' so we don't load the elfie dll until actually necessary. + 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)) + object databaseObj; + if (!_sourceToDatabase.TryGetValue(source, out databaseObj)) { // Don't have a database to search. yield break; } + var database = databaseObj as AddReferenceDatabase; 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)) + object databaseObj; + if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out databaseObj)) { // Don't have a database to search. yield break; } + var database = (AddReferenceDatabase)databaseObj; if (name == "var") { // never find anything named 'var'. From a7dc3b60b2aca7dec7e7dd44a401858fac5c2aee Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 12 May 2016 09:48:05 -0700 Subject: [PATCH 10/28] Wrapping all `DTE` calls in `IntegrationHelper.RetryRpcCall`, as they tend to fail with `RPC_E_CALL_REJECTED`. --- .../TestUtilities/Extensions/DteExtensions.cs | 16 +- .../IntegrationServiceExtensions.cs | 6 +- .../TestUtilities/IntegrationHelper.cs | 243 ++++++++++++++---- .../TestUtilities/Interop/Kernel32.cs | 13 + .../TestUtilities/Interop/User32.cs | 66 ++++- .../TestUtilities/Remoting/RemotingHelper.cs | 12 + .../TestUtilities/VisualStudioInstance.cs | 37 +-- .../VisualStudioTestUtilities.csproj | 1 + .../TestUtilities/Window/EditorWindow.cs | 49 ++-- .../TestUtilities/Window/InteractiveWindow.cs | 5 +- .../TestUtilities/Window/SolutionExplorer.cs | 8 +- .../TestUtilities/Workspace/Solution.cs | 16 +- 12 files changed, 354 insertions(+), 118 deletions(-) create mode 100644 src/VisualStudio/TestUtilities/Interop/Kernel32.cs diff --git a/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs index fb2279bcf4f99..45264ceb05ef1 100644 --- a/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs +++ b/src/VisualStudio/TestUtilities/Extensions/DteExtensions.cs @@ -13,22 +13,26 @@ public static async Task ExecuteCommandAsync(this DTE dte, string command, strin // to something more logical, like null, would change the expected behavior of Dte.ExecuteCommand await dte.WaitForCommandAvailabilityAsync(command).ConfigureAwait(continueOnCapturedContext: false); - IntegrationHelper.RetryDteCall(() => dte.ExecuteCommand(command, args)); + IntegrationHelper.RetryRpcCall(() => dte.ExecuteCommand(command, args)); } - public static Task LocateWindowAsync(this DTE dte, string windowTitle) => IntegrationHelper.WaitForNotNullAsync(() => IntegrationHelper.RetryDteCall(() => + public static Window LocateWindow(this DTE dte, string windowTitle) { - foreach (Window window in dte.Windows) + var dteWindows = IntegrationHelper.RetryRpcCall(() => dte.Windows); + + foreach (Window window in dteWindows) { - if (window.Caption.Equals(windowTitle)) + 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.RetryDteCall(() => dte.Commands.Item(command).IsAvailable), expectedResult: true); + => 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 index d29b6b12ee267..a0a0d56e88c68 100644 --- a/src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs +++ b/src/VisualStudio/TestUtilities/Extensions/IntegrationServiceExtensions.cs @@ -15,9 +15,9 @@ public static T Execute(this IntegrationService integrationService, Type type public static void 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); + var result = integrationService.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); - if (!string.IsNullOrWhiteSpace(objectUri)) + if (result != null) { throw new InvalidOperationException("The specified call was not expected to return a value."); } @@ -27,7 +27,7 @@ public static T Execute(this IntegrationService integrationService, string as { var objectUri = integrationService.Execute(assemblyFilePath, typeFullName, methodName, bindingFlags, parameters); - if (string.IsNullOrWhiteSpace(objectUri)) + if (objectUri == null) { throw new InvalidOperationException("The specified call was expected to return a value."); } diff --git a/src/VisualStudio/TestUtilities/IntegrationHelper.cs b/src/VisualStudio/TestUtilities/IntegrationHelper.cs index 24be9b2e7fd27..ba84b2672dc61 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 == -2147024891) // E_ACCESS_DENIED + { + 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: '{registryKey}'"); + } + + 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,9 +202,106 @@ public static void KillProcess(string processName) } } - public static void SetFocus(IntPtr windowHandle) + public static void RetryRpcCall(Action action) { - User32.SetForegroundWindow(windowHandle); + 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. @@ -177,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}'"); - } + var success = User32.BlockInput(false); - return registryKey.GetValue(valueName); - } - } - - 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); } } @@ -213,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/Interop/Kernel32.cs b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs new file mode 100644 index 0000000000000..d09621c0822a9 --- /dev/null +++ b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.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 0442f8dc2499c..3fab7a12a3fcd 100644 --- a/src/VisualStudio/TestUtilities/Interop/User32.cs +++ b/src/VisualStudio/TestUtilities/Interop/User32.cs @@ -22,15 +22,37 @@ 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; @@ -102,6 +124,14 @@ public struct MOUSEINPUT [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( @@ -111,7 +141,7 @@ public struct MOUSEINPUT [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 ); @@ -136,6 +166,18 @@ public struct MOUSEINPUT [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, @@ -159,12 +201,34 @@ public struct MOUSEINPUT [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/RemotingHelper.cs b/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs index a2689ce68943f..b281efe756935 100644 --- a/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs +++ b/src/VisualStudio/TestUtilities/Remoting/RemotingHelper.cs @@ -119,6 +119,18 @@ private static IVsTextView VsTextManagerActiveView private static TestingOnly_WaitingService WaitingService => DefaultComponentModelExportProvider.GetExport().Value; + public static void ActivateMainWindow() => InvokeOnUIThread(() => + { + var activeVisualStudioWindow = (IntPtr)(IntegrationHelper.RetryRpcCall(() => DTE.ActiveWindow.HWnd)); + + if (activeVisualStudioWindow == IntPtr.Zero) + { + activeVisualStudioWindow = (IntPtr)(IntegrationHelper.RetryRpcCall(() => DTE.MainWindow.HWnd)); + } + + IntegrationHelper.SetForegroundWindow(activeVisualStudioWindow); + }); + public static void CleanupWaitingService() { var asynchronousOperationWaiterExports = DefaultComponentModelExportProvider.GetExports(); diff --git a/src/VisualStudio/TestUtilities/VisualStudioInstance.cs b/src/VisualStudio/TestUtilities/VisualStudioInstance.cs index 8aa8ca061a63a..e7570d1eb8d43 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioInstance.cs +++ b/src/VisualStudio/TestUtilities/VisualStudioInstance.cs @@ -108,44 +108,45 @@ public void Cleanup() private void CleanupInteractiveWindow() { - foreach (Window window in Dte.Windows) - { - if (window.Caption == CSharpInteractiveWindow.DteWindowTitle) - { - window.Close(); - break; - } - } + var csharpInteractiveWindow = _dte.LocateWindow(CSharpInteractiveWindow.DteWindowTitle); + IntegrationHelper.RetryRpcCall(() => csharpInteractiveWindow?.Close()); } private void CleanupOpenSolution() { - IntegrationHelper.RetryDteCall(() => _dte.Documents.CloseAll(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) { @@ -174,7 +175,7 @@ public void Close() private void CloseHostProcess() { - IntegrationHelper.RetryDteCall(() => _dte.Quit()); + IntegrationHelper.RetryRpcCall(() => _dte.Quit()); IntegrationHelper.KillProcess(_hostProcess); } @@ -183,7 +184,7 @@ private void CloseRemotingService() { try { - if ((IntegrationHelper.RetryDteCall(() => _dte?.Commands.Item(VisualStudioCommandNames.VsStopServiceCommand).IsAvailable).GetValueOrDefault())) + if ((IntegrationHelper.RetryRpcCall(() => _dte?.Commands.Item(VisualStudioCommandNames.VsStopServiceCommand).IsAvailable).GetValueOrDefault())) { _dte.ExecuteCommandAsync(VisualStudioCommandNames.VsStopServiceCommand).GetAwaiter().GetResult(); } diff --git a/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj b/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj index 00893384f9591..d6c5ce41efdac 100644 --- a/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj +++ b/src/VisualStudio/TestUtilities/VisualStudioTestUtilities.csproj @@ -21,6 +21,7 @@ + diff --git a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs index e39952d359312..a0f68008b3e72 100644 --- a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs @@ -86,9 +86,9 @@ public string Text } } - private TextSelection TextSelection => (TextSelection)(_visualStudioInstance.Dte.ActiveDocument.Selection); + private TextSelection TextSelection => (TextSelection)(IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.ActiveDocument.Selection)); - public void Activate() => _visualStudioInstance.Dte.ActiveDocument.Activate(); + public void Activate() => IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.ActiveDocument.Activate()); public void ClearTextSelection() => TextSelection?.Cancel(); @@ -97,7 +97,7 @@ public void Find(string expectedText) Activate(); ClearTextSelection(); - var dteFind = _visualStudioInstance.Dte.Find; + var dteFind = IntegrationHelper.RetryRpcCall(() => _visualStudioInstance.Dte.Find); dteFind.Action = vsFindAction.vsFindActionFind; dteFind.FindWhat = expectedText; @@ -107,7 +107,7 @@ public void Find(string expectedText) dteFind.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral; dteFind.Target = vsFindTarget.vsFindTargetCurrentDocument; - var findResult = dteFind.Execute(); + var findResult = IntegrationHelper.RetryRpcCall(() => dteFind.Execute()); if (findResult != vsFindResult.vsFindResultFound) { @@ -130,22 +130,15 @@ private async Task SendKeysAsync(string text, int wordsPerMinute) foreach (var character in text) { - var foregroundWindowHandle = IntPtr.Zero; + var foregroundWindow = IntPtr.Zero; var inputBlocked = false; try { - inputBlocked = User32.BlockInput(true); - foregroundWindowHandle = User32.GetForegroundWindow(); + inputBlocked = IntegrationHelper.BlockInput(); + foregroundWindow = IntegrationHelper.GetForegroundWindow(); - var activeWindowHandle = (IntPtr)(_visualStudioInstance.Dte.ActiveWindow.HWnd); - - if (activeWindowHandle == IntPtr.Zero) - { - activeWindowHandle = (IntPtr)(_visualStudioInstance.Dte.MainWindow.HWnd); - } - - IntegrationHelper.SetFocus(activeWindowHandle); + _visualStudioInstance.IntegrationService.Execute(typeof(RemotingHelper), nameof(RemotingHelper.ActivateMainWindow)); var vk = User32.VkKeyScan(character); @@ -157,17 +150,17 @@ private async Task SendKeysAsync(string text, int wordsPerMinute) { if ((vk & 0x0100) != 0) // SHIFT { - SendKey(User32.VK_SHIFT); + SendKey(User32.VK_SHIFT, User32.KEYEVENTF_NONE); } if ((vk & 0x0200) != 0) // CTRL { - SendKey(User32.VK_CONTROL); + SendKey(User32.VK_CONTROL, User32.KEYEVENTF_NONE); } if ((vk & 0x0400) != 0) // ALT { - SendKey(User32.VK_MENU); + SendKey(User32.VK_MENU, User32.KEYEVENTF_NONE); } SendKey((ushort)(vk & 0xFF)); @@ -190,14 +183,14 @@ private async Task SendKeysAsync(string text, int wordsPerMinute) } finally { - if (foregroundWindowHandle != IntPtr.Zero) + if (foregroundWindow != IntPtr.Zero) { - IntegrationHelper.SetFocus(foregroundWindowHandle); + IntegrationHelper.SetForegroundWindow(foregroundWindow); } if (inputBlocked) { - User32.BlockInput(false); + IntegrationHelper.UnblockInput(); } } @@ -209,13 +202,13 @@ private async Task SendKeysAsync(string text, int wordsPerMinute) private void SendKey(ushort vk) { - SendKey(vk); + SendKey(vk, User32.KEYEVENTF_NONE); SendKey(vk, User32.KEYEVENTF_KEYUP); } - private void SendKey(ushort vk, uint dwFlags = 0) + private void SendKey(ushort vk, uint dwFlags) { - var inputs = new User32.INPUT[] { + var input = new User32.INPUT[] { new User32.INPUT() { Type = User32.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT() { @@ -230,15 +223,15 @@ private void SendKey(ushort vk, uint dwFlags = 0) if (IsExtendedKey(vk)) { - inputs[0].ki.dwFlags |= User32.KEYEVENTF_EXTENDEDKEY; + input[0].ki.dwFlags |= User32.KEYEVENTF_EXTENDEDKEY; } - User32.SendInput(1, inputs, User32.SizeOf_INPUT); + IntegrationHelper.SendInput(input); } private void SendCharacter(char character) { - var inputs = new User32.INPUT[] { + var input = new User32.INPUT[] { new User32.INPUT() { Type = User32.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT() { @@ -261,7 +254,7 @@ private void SendCharacter(char character) } }; - User32.SendInput(2, inputs, User32.SizeOf_INPUT); + IntegrationHelper.SendInput(input); } } } diff --git a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs index 4f2e1f95ff077..dc3c888c5353c 100644 --- a/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/InteractiveWindow.cs @@ -25,9 +25,8 @@ internal InteractiveWindow(VisualStudioInstance visualStudioInstance, string dte // We have to show the window at least once to ensure the interactive service is loaded. ShowAsync(waitForPrompt: false).GetAwaiter().GetResult(); - var dte = _visualStudioInstance.Dte; - var dteWindow = dte.LocateWindowAsync(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 var integrationService = _visualStudioInstance.IntegrationService; diff --git a/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs b/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs index 4a783f42531ca..290308efc63ff 100644 --- a/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs +++ b/src/VisualStudio/TestUtilities/Window/SolutionExplorer.cs @@ -23,9 +23,9 @@ internal SolutionExplorer(VisualStudioInstance visualStudio) /// 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(saveExistingSolutionIfExists); } @@ -33,12 +33,12 @@ public Solution CreateSolution(string solutionName, bool saveExistingSolutionIfE var solutionPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); IntegrationHelper.DeleteDirectoryRecursively(solutionPath); - dteSolution.Create(solutionPath, solutionName); + 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/Solution.cs b/src/VisualStudio/TestUtilities/Workspace/Solution.cs index 6ebd4ecabed4d..5cabfef88a9e8 100644 --- a/src/VisualStudio/TestUtilities/Workspace/Solution.cs +++ b/src/VisualStudio/TestUtilities/Workspace/Solution.cs @@ -54,7 +54,7 @@ public string FileName { get { - var solutionFullName = _dteSolution.FullName; + var solutionFullName = IntegrationHelper.RetryRpcCall(() => _dteSolution.FullName); return string.IsNullOrEmpty(solutionFullName) ? _fileName : solutionFullName; } } @@ -64,13 +64,17 @@ public Project AddProject(string projectName, ProjectTemplate projectTemplate, P 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) { - foreach (DteProject project in DteSolution.Projects) + var dteSolutionProjects = IntegrationHelper.RetryRpcCall(() => _dteSolution.Projects); + + foreach (DteProject project in dteSolutionProjects) { - if (project.Name == projectName) + var dteProjectName = IntegrationHelper.RetryRpcCall(() => project.Name); + + if (dteProjectName == projectName) { dteProject = project; } @@ -83,11 +87,11 @@ public Project AddProject(string projectName, ProjectTemplate projectTemplate, P public void Save() { Directory.CreateDirectory(DirectoryName); - _dteSolution.SaveAs(FileName); + 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(_projectTemplates[projectTemplate], ProjectLanguages[projectLanguage]); + => IntegrationHelper.RetryRpcCall(() => _dteSolution.GetProjectTemplate(_projectTemplates[projectTemplate], ProjectLanguages[projectLanguage])); } } From db7d89af6e43c6ec2e74e50eb1c2aefb55d114fb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 12 May 2016 13:24:48 -0700 Subject: [PATCH 11/28] Delay load in a typesafe manner. --- .../Core/Def/ServicesVisualStudio.csproj | 1 + .../IAddReferenceDatabaseWrapper.cs | 25 +++++++++++++++++++ .../SymbolSearchService.Update.cs | 2 +- .../Def/SymbolSearch/SymbolSearchService.cs | 16 ++++++------ 4 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 src/VisualStudio/Core/Def/SymbolSearch/IAddReferenceDatabaseWrapper.cs diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index eca7578c97edd..cd4008a19ea6c 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -155,6 +155,7 @@ + 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 fd5dcc94d2b08..894bdef8002d1 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.Update.cs @@ -423,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 6a2f13e2f5dcc..1da89cb57f852 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/SymbolSearchService.cs @@ -36,8 +36,8 @@ internal partial class SymbolSearchService : ISymbolSearchService, IDisposable { - // Value is typed as 'object' so we don't load the elfie dll until actually necessary. - private ConcurrentDictionary _sourceToDatabase = new ConcurrentDictionary(); + private ConcurrentDictionary _sourceToDatabase = + new ConcurrentDictionary(); public SymbolSearchService( VSShell.SVsServiceProvider serviceProvider, @@ -115,14 +115,14 @@ private static IRemoteControlService CreateRemoteControlService(VSShell.SVsServi public IEnumerable FindPackagesWithType( string source, string name, int arity, CancellationToken cancellationToken) { - object databaseObj; - if (!_sourceToDatabase.TryGetValue(source, out databaseObj)) + IAddReferenceDatabaseWrapper databaseWrapper; + if (!_sourceToDatabase.TryGetValue(source, out databaseWrapper)) { // Don't have a database to search. yield break; } - var database = databaseObj as AddReferenceDatabase; + var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. @@ -189,14 +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. - object databaseObj; - if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out databaseObj)) + IAddReferenceDatabaseWrapper databaseWrapper; + if (!_sourceToDatabase.TryGetValue(NugetOrgSource, out databaseWrapper)) { // Don't have a database to search. yield break; } - var database = (AddReferenceDatabase)databaseObj; + var database = databaseWrapper.Database; if (name == "var") { // never find anything named 'var'. From 129df995a809de934489cabb631a4d2aeaa8de48 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Thu, 12 May 2016 15:27:40 -0700 Subject: [PATCH 12/28] Responding to PR feedback. --- .../CSharp/CSharpAutomaticBraceCompletion.cs | 17 ++++--- .../TestUtilities/IntegrationHelper.cs | 4 +- .../TestUtilities/Interop/Kernel32.cs | 3 +- .../TestUtilities/Window/EditorWindow.cs | 46 +++++++++++++++++++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs index b5fbbfae8f35d..8115e68393410 100644 --- a/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs +++ b/src/VisualStudio/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs @@ -52,7 +52,7 @@ public async Task BracesInsertionAndTabCompleting() Assert.Equal(" if (true) { ", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal("}", _editorWindow.CurrentLineTextAfterCursor); - await _editorWindow.TypeTextAsync("\t"); + await _editorWindow.TypeTextAsync($"{EditorWindow.TAB}"); Assert.Equal(" if (true) { }", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); @@ -88,7 +88,8 @@ public async Task BracesOnReturnNoFormattingOnlyIndentationBeforeCloseBrace() _editorWindow.PlaceCursor("// Marker"); await _editorWindow.TypeTextAsync("if (true) {"); - await _editorWindow.TypeTextAsync("\nvar a = 1;"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); + await _editorWindow.TypeTextAsync("var a = 1;"); Assert.Equal(" var a = 1;", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); @@ -106,7 +107,8 @@ public async Task BracesOnReturnOvertypingTheClosingBrace() _editorWindow.PlaceCursor("// Marker"); await _editorWindow.TypeTextAsync("if (true) {"); - await _editorWindow.TypeTextAsync("\nvar a = 1;}"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); + await _editorWindow.TypeTextAsync("var a = 1;}"); Assert.Equal(" }", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); @@ -123,7 +125,9 @@ public async Task BracesOnReturnOvertypingTheClosingBrace() public async Task BracesOnReturnWithNonWhitespaceSpanInside() { _editorWindow.Text = string.Empty; - await _editorWindow.TypeTextAsync("class A { int i;\n"); + + await _editorWindow.TypeTextAsync("class A { int i;"); + await _editorWindow.TypeTextAsync($"{EditorWindow.ENTER}"); Assert.Equal(string.Empty, _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal("}", _editorWindow.CurrentLineTextAfterCursor); @@ -146,7 +150,8 @@ public async Task ParenInsertionAndTabCompleting() Assert.Equal(" void Foo(", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal(")", _editorWindow.CurrentLineTextAfterCursor); - await _editorWindow.TypeTextAsync("int x\t"); + await _editorWindow.TypeTextAsync("int x"); + await _editorWindow.TypeTextAsync($"{EditorWindow.TAB}"); Assert.Equal(" void Foo(int x)", _editorWindow.CurrentLineTextBeforeCursor); Assert.Equal(string.Empty, _editorWindow.CurrentLineTextAfterCursor); @@ -162,7 +167,7 @@ public async Task ParenOvertyping() _editorWindow.PlaceCursor("// Marker"); await _editorWindow.TypeTextAsync("void Foo("); - await _editorWindow.TypeTextAsync("\u001B"); // ESC + await _editorWindow.TypeTextAsync($"{EditorWindow.ESC}"); await _editorWindow.TypeTextAsync(")"); Assert.Equal(" void Foo()", _editorWindow.CurrentLineTextBeforeCursor); diff --git a/src/VisualStudio/TestUtilities/IntegrationHelper.cs b/src/VisualStudio/TestUtilities/IntegrationHelper.cs index ba84b2672dc61..95ee98862f1e2 100644 --- a/src/VisualStudio/TestUtilities/IntegrationHelper.cs +++ b/src/VisualStudio/TestUtilities/IntegrationHelper.cs @@ -44,7 +44,7 @@ public static bool BlockInput() { var hresult = Marshal.GetHRForLastWin32Error(); - if (hresult == -2147024891) // E_ACCESS_DENIED + if (hresult == VSConstants.E_ACCESSDENIED) { Debug.WriteLine("Input cannot be blocked because the system requires Administrative privileges."); } @@ -140,7 +140,7 @@ public static object GetRegistryKeyValue(RegistryKey baseKey, string subKeyName, { if (registryKey == null) { - throw new Exception($"The specified registry key could not be found. Registry Key: '{registryKey}'"); + throw new Exception($@"The specified registry key could not be found. Registry Key: '{baseKey}\{subKeyName}'"); } return registryKey.GetValue(valueName); diff --git a/src/VisualStudio/TestUtilities/Interop/Kernel32.cs b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs index d09621c0822a9..1d9d1a7e738a1 100644 --- a/src/VisualStudio/TestUtilities/Interop/Kernel32.cs +++ b/src/VisualStudio/TestUtilities/Interop/Kernel32.cs @@ -7,7 +7,6 @@ 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( - ); + public static extern uint GetCurrentThreadId(); } } diff --git a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs index a0f68008b3e72..93df5d7b8e881 100644 --- a/src/VisualStudio/TestUtilities/Window/EditorWindow.cs +++ b/src/VisualStudio/TestUtilities/Window/EditorWindow.cs @@ -12,6 +12,52 @@ namespace Roslyn.VisualStudio.Test.Utilities /// Provides a means of interacting with the active editor window in the Visual Studio host. public class EditorWindow { + 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'; + private readonly VisualStudioInstance _visualStudioInstance; private readonly EditorWindowWrapper _editorWindowWrapper; From eea2452c8719e6553d21dd0aa893c6d60874a636 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Fri, 22 Apr 2016 20:48:50 -0700 Subject: [PATCH 13/28] Get build working with VS "15" again 1. Reference a bunch of stuff from Nuget packages instead of VS. 2. Move to Update 2 versions of packages. 3. Now that we are using the packages, we're referencing Microsoft.VisualStudio.Shell.14.dll instead of .15 again. This means that types that moved to Shell.Immutable.15 are not type forwarders. Luckily we didn't need anything from Shell.Immutable.15 in the project where it was a problem. --- src/Dependencies/VisualStudio/project.json | 10 +++++----- .../VisualStudioEditor/project.json | 10 +++++----- .../VisualStudioText/project.json | 11 +++++----- .../CSharpEditorServicesTest.csproj | 6 +----- .../CSharpEditorServicesTest2.csproj | 6 +----- src/EditorFeatures/Core/EditorFeatures.csproj | 7 +------ src/EditorFeatures/Core/project.json | 2 ++ .../Test/EditorServicesTest.csproj | 11 +--------- src/EditorFeatures/Test/project.json | 1 + .../Test2/EditorServicesTest2.vbproj | 5 +---- .../Text/TextEditorFeatures.csproj | 5 +---- .../BasicEditorServicesTest.vbproj | 5 +---- .../Editor/InteractiveWindow.csproj | 6 +----- .../EditorTest/InteractiveWindowTest.csproj | 9 +-------- src/InteractiveWindow/EditorTest/project.json | 3 ++- .../CSharp/Impl/CSharpVisualStudio.csproj | 3 +-- .../CSharp/Test/CSharpVisualStudioTest.csproj | 10 +--------- .../Core/Def/ServicesVisualStudio.csproj | 20 +------------------ src/VisualStudio/Core/Def/project.json | 3 +++ .../Core/Test/ServicesVisualStudioTest.vbproj | 17 +++++----------- .../VisualBasic/Impl/BasicVisualStudio.vbproj | 3 +-- 21 files changed, 42 insertions(+), 111 deletions(-) diff --git a/src/Dependencies/VisualStudio/project.json b/src/Dependencies/VisualStudio/project.json index 7c4524f5913f9..d4435b29019a3 100644 --- a/src/Dependencies/VisualStudio/project.json +++ b/src/Dependencies/VisualStudio/project.json @@ -2,14 +2,14 @@ "supports": { }, "dependencies": { "Microsoft.VisualStudio.Designer.Interfaces": "1.1.4322", - "Microsoft.VisualStudio.Editor": "14.1.24720", + "Microsoft.VisualStudio.Editor": "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", @@ -18,4 +18,4 @@ "frameworks": { ".NETFramework,Version=v4.5": { } } -} \ No newline at end of file +} diff --git a/src/Dependencies/VisualStudioEditor/project.json b/src/Dependencies/VisualStudioEditor/project.json index 5dabd7a19ad89..7c6a1b5414068 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.5": { } diff --git a/src/Dependencies/VisualStudioText/project.json b/src/Dependencies/VisualStudioText/project.json index 3616d45abaa75..c7c228ec39ad1 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.5": { } } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index b2f16ebe691eb..0aa439ec5629c 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -105,10 +105,6 @@ - - - - @@ -509,4 +505,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj index ad75f2737d1b4..dbc2cbe1f9efd 100644 --- a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj +++ b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj @@ -109,10 +109,6 @@ - - - - @@ -264,4 +260,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 24406245cf587..034138ec7d15c 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 - - - @@ -800,4 +795,4 @@ - \ 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/Test/EditorServicesTest.csproj b/src/EditorFeatures/Test/EditorServicesTest.csproj index ad5232980dbc9..672305816fcee 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 @@ - - - - @@ -349,4 +340,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 b5be4d7cd77e5..82da3c67a9606 100644 --- a/src/EditorFeatures/Test2/EditorServicesTest2.vbproj +++ b/src/EditorFeatures/Test2/EditorServicesTest2.vbproj @@ -116,9 +116,6 @@ Roslyn.Services.Editor.UnitTests2.xml - - - @@ -295,4 +292,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/Text/TextEditorFeatures.csproj b/src/EditorFeatures/Text/TextEditorFeatures.csproj index 12d886b6fa94e..672ef17847b29 100644 --- a/src/EditorFeatures/Text/TextEditorFeatures.csproj +++ b/src/EditorFeatures/Text/TextEditorFeatures.csproj @@ -32,9 +32,6 @@ - - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Text.Internal.dll - @@ -93,4 +90,4 @@ - \ No newline at end of file + diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index 5fbaf4ceb8cbf..ead6f4836d7d2 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -91,9 +91,6 @@ true - - - @@ -611,4 +608,4 @@ - \ No newline at end of file + diff --git a/src/InteractiveWindow/Editor/InteractiveWindow.csproj b/src/InteractiveWindow/Editor/InteractiveWindow.csproj index a1e19d57de745..4ef443e06c554 100644 --- a/src/InteractiveWindow/Editor/InteractiveWindow.csproj +++ b/src/InteractiveWindow/Editor/InteractiveWindow.csproj @@ -27,10 +27,6 @@ - - False - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.Text.Internal.dll - @@ -137,4 +133,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/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj index 84732504ae804..301917cd73e06 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj +++ b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj @@ -93,7 +93,6 @@ false - @@ -268,4 +267,4 @@ - \ No newline at end of file + 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/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index cd4008a19ea6c..db5fbbc8a8e2f 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -249,9 +249,6 @@ - - $(DevEnvDir)\Microsoft.VisualStudio.CallHierarchy.Package.Definitions.dll - $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.dll @@ -298,21 +295,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 - @@ -757,4 +739,4 @@ - \ No newline at end of file + 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/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index 8705b9dc11f7d..48c0f12c47557 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -125,13 +125,6 @@ false - - - - - - false - @@ -146,16 +139,16 @@ - - + in the solution explorer since it doesn't think it resolved. By making this a target, we work around that issue. --> @@ -404,4 +397,4 @@ - \ No newline at end of file + diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj index b68f7a935c819..7987d884da28e 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj +++ b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj @@ -188,7 +188,6 @@ false - @@ -239,4 +238,4 @@ - \ No newline at end of file + From e476b58a479208687e6602d05599938cd91a6f02 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Wed, 4 May 2016 19:09:08 -0700 Subject: [PATCH 14/28] Avoid hard-coded 14.0 --- src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs | 5 ++++- .../Core/MSBuildTaskTests/MSBuildTaskTests.csproj | 5 +++-- src/Compilers/Core/MSBuildTaskTests/project.json | 2 +- .../Server/VBCSCompilerTests/CompilerServerTests.cs | 5 ++++- .../ResultProvider/DebuggerVisualizerAttributeTests.cs | 4 +++- .../Test/ResultProvider/Debugger/Engine/DkmClrType.cs | 3 ++- src/Test/Perf/bootstrap.bat | 6 +++--- src/Test/Perf/infra/install.csx | 8 +++++--- .../AnalyzerSupport/AnalyzerDependencyCheckerTests.vb | 5 ++++- 9 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs index 2e275d810df80..0dff21e338b7d 100644 --- a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs @@ -16,6 +16,8 @@ using System.Xml; using System.Threading.Tasks; +using static System.FormattableString; + namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests { public class IntegrationTests : TestBase @@ -25,7 +27,8 @@ public class IntegrationTests : TestBase static IntegrationTests() { - using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0", writable: false)) + var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; + using (var key = Registry.LocalMachine.OpenSubKey(Invariant($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}"), writable: false)) { if (key != null) { diff --git a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj index 87ff520c940ed..dab9e06ca6d92 100644 --- a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj +++ b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj @@ -1,6 +1,6 @@  - + @@ -15,7 +15,8 @@ true ..\..\..\..\ true - v4.5 + v4.6 + diff --git a/src/Compilers/Core/MSBuildTaskTests/project.json b/src/Compilers/Core/MSBuildTaskTests/project.json index 10bae1671e950..421068a899445 100644 --- a/src/Compilers/Core/MSBuildTaskTests/project.json +++ b/src/Compilers/Core/MSBuildTaskTests/project.json @@ -2,7 +2,7 @@ "dependencies": { }, "frameworks": { - "net45": {} + "net46": {} }, "runtimes": { "win7": { } diff --git a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index 8d63a6627b244..f1d98684dcb3e 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -22,6 +22,8 @@ using Microsoft.CodeAnalysis.CommandLine; using Moq; +using static System.FormattableString; + namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests { public class CompilerServerUnitTests : TestBase @@ -76,7 +78,8 @@ private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs e) private static string GetMSBuildDirectory() { - using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0", false)) + var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; + using (var key = Registry.LocalMachine.OpenSubKey(Invariant($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}"), false)) { if (key != null) { 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/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/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") From 8f39e2dbcbe1cb933f33d2910ce63762a6a5d61d Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 5 May 2016 15:07:30 -0700 Subject: [PATCH 15/28] Force generation of binding redirects so that they are version dependent --- build/Targets/VSL.Imports.targets | 32 +++++++++++++++++++ .../Core/Test/ServicesVisualStudioTest.vbproj | 6 ++++ src/Workspaces/CoreTest/ServicesTest.csproj | 2 ++ src/Workspaces/CoreTest/app.config | 32 ------------------- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/build/Targets/VSL.Imports.targets b/build/Targets/VSL.Imports.targets index 5cb65d58f0c27..addb03cea5812 100644 --- a/build/Targets/VSL.Imports.targets +++ b/build/Targets/VSL.Imports.targets @@ -74,6 +74,38 @@ + + + + $(VisualStudioReferenceAssemblyVersion) + + + $(VisualStudioReferenceAssemblyVersion) + + + $(VisualStudioReferenceAssemblyVersion) + + + + true + true + + + + + + + true + + + AnyCPU v4.6 + true + + + $(VisualStudioReferenceAssemblyVersion) + + {1EE8CAD3-55F9-4D91-96B2-084641DA9A6C} diff --git a/src/Workspaces/CoreTest/ServicesTest.csproj b/src/Workspaces/CoreTest/ServicesTest.csproj index 3d32b55766149..eceeaaebefd88 100644 --- a/src/Workspaces/CoreTest/ServicesTest.csproj +++ b/src/Workspaces/CoreTest/ServicesTest.csproj @@ -13,7 +13,9 @@ Microsoft.CodeAnalysis.UnitTests Roslyn.Services.UnitTests v4.5.2 + 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 07cf56fdfe76868421ff292c9e3d217aee9ff3a6 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Sat, 23 Apr 2016 15:27:43 -0700 Subject: [PATCH 16/28] Introduce 'EditorFeatures.Next' project for Dev15 specific code --- Roslyn.sln | 31 +++++++- .../Next/EditorFeatures.Next.csproj | 74 +++++++++++++++++++ src/EditorFeatures/Next/PublicAPI.Shipped.txt | 0 .../Next/PublicAPI.Unshipped.txt | 0 src/EditorFeatures/Next/project.json | 7 ++ 5 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 src/EditorFeatures/Next/EditorFeatures.Next.csproj create mode 100644 src/EditorFeatures/Next/PublicAPI.Shipped.txt create mode 100644 src/EditorFeatures/Next/PublicAPI.Unshipped.txt create mode 100644 src/EditorFeatures/Next/project.json diff --git a/Roslyn.sln b/Roslyn.sln index c64797272c616..3305b8dac1c8c 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,8 @@ 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 Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Test\Utilities\Shared\TestUtilities.projitems*{76c6f005-c89d-4348-bb4a-391898dbeb52}*SharedItemsImports = 4 @@ -385,12 +387,12 @@ Global src\Compilers\Core\CommandLine\CommandLine.projitems*{ad6f474e-e6d4-4217-91f3-b7af1be31ccc}*SharedItemsImports = 13 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 - src\Compilers\Server\ServerShared\ServerShared.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 + src\Compilers\Server\ServerShared\ServerShared.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 4 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 4 - src\Compilers\Server\ServerShared\ServerShared.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 + src\Compilers\Server\ServerShared\ServerShared.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 4 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 @@ -3130,6 +3132,26 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3296,5 +3318,6 @@ 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} EndGlobalSection EndGlobal diff --git a/src/EditorFeatures/Next/EditorFeatures.Next.csproj b/src/EditorFeatures/Next/EditorFeatures.Next.csproj new file mode 100644 index 0000000000000..44343d919bf4e --- /dev/null +++ b/src/EditorFeatures/Next/EditorFeatures.Next.csproj @@ -0,0 +1,74 @@ + + + + CSharp + true + + + + + + Debug + AnyCPU + {366bbcdc-b05f-4677-9b5b-78ba816a1484} + Library + Microsoft.CodeAnalysis.Editor + Microsoft.CodeAnalysis.EditorFeatures.Next + v4.6 + false + + + + {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 + + + {18F5FBB8-7570-4412-8CC7-0A86FF13B7BA} + TextEditorFeatures + + + {01E9BD68-0339-4A13-B42F-A3CA84D164F3} + InteractiveWindow + InteractiveWindow + + + + true + + + true + + + + + + + + + + + + + + + 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..718f042f58ddd --- /dev/null +++ b/src/EditorFeatures/Next/project.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + }, + "frameworks": { + "net46": {} + } +} From 6d56b833086f88a82dd345394f31bdf567e7bf98 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Sat, 23 Apr 2016 19:15:48 -0700 Subject: [PATCH 17/28] Factor new Completion types into EditorFeatures.Next More cleanup to do between RoslynCompletionSet and FilteredRoslynCompletionSet. --- src/EditorFeatures/Core/EditorFeatures.csproj | 12 ++-- .../VisualStudioVersionMetadata.cs | 16 +++++ .../ExportVersionSpecificAttribute.cs | 27 ++++++++ .../Core/Extensibility/VersionSelector.cs | 26 ++++++++ .../Presentation/CompletionPresenter.cs | 13 +++- .../CompletionPresenterSession.cs | 14 ++--- .../Completion/Presentation/ICompletionSet.cs | 28 +++++++++ .../Presentation/ICompletionSetFactory.cs | 13 ++++ ...mpletionSet3.cs => RoslynCompletionSet.cs} | 47 +++++++------- .../VisualStudio14CompletionSetFactory.cs | 19 ++++++ .../Temporary/IIntellisenseFilter.cs | 47 -------------- .../Temporary/IntellisenseFilter.cs | 61 ------------------- .../Next/EditorFeatures.Next.csproj | 16 ++++- .../Presentation/IntellisenseFilter2.cs | 4 +- .../VisualStudio15CompletionSetFactory.cs | 18 ++++++ src/EditorFeatures/Next/project.json | 1 + .../Text/TextEditorFeatures.csproj | 1 + src/Features/Core/Portable/Features.csproj | 1 + .../Presentation/CompletionPresenter.cs | 13 +++- 19 files changed, 228 insertions(+), 149 deletions(-) create mode 100644 src/EditorFeatures/Core/Extensibility/Composition/VisualStudioVersionMetadata.cs create mode 100644 src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs create mode 100644 src/EditorFeatures/Core/Extensibility/VersionSelector.cs create mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSet.cs create mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs rename src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/{CompletionSet3.cs => RoslynCompletionSet.cs} (91%) create mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs delete mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IIntellisenseFilter.cs delete mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IntellisenseFilter.cs rename src/EditorFeatures/{Core/Implementation => Next}/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs (88%) create mode 100644 src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 034138ec7d15c..580ad869012a0 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -75,6 +75,7 @@ + @@ -222,6 +223,8 @@ + + @@ -253,6 +256,7 @@ + @@ -268,9 +272,10 @@ - - - + + + + @@ -475,7 +480,6 @@ - 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..41617709dca12 --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Editor +{ + internal enum VisualStudioVersion + { + VS2015 = 14, + VS15 = 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..48e41cae5e83e --- /dev/null +++ b/src/EditorFeatures/Core/Extensibility/VersionSelector.cs @@ -0,0 +1,26 @@ +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 Select(IEnumerable> items) + { + var best = items.FirstOrDefault(); + foreach (var item in items.Skip(1)) + { + if ((int)item.Metadata.Version > (int)best.Metadata.Version) + { + best = item; + } + } + + return best.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..97d465535af16 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.Select(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 026d46f774166..96daf0cfc94cb 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenterSession.cs @@ -29,7 +29,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; @@ -45,6 +45,7 @@ public ITextBuffer SubjectBuffer } public CompletionPresenterSession( + ICompletionSetFactory completionSetFactory, ICompletionBroker completionBroker, IGlyphService glyphService, ITextView textView, @@ -55,7 +56,7 @@ public ITextBuffer SubjectBuffer _textView = textView; _subjectBuffer = subjectBuffer; - _completionSet = new CompletionSet3(this, textView, subjectBuffer); + _completionSet = completionSetFactory.CreateCompletionSet(this, textView, subjectBuffer); _completionSet.SelectionStatusChanged += OnCompletionSetSelectionStatusChanged; } @@ -157,15 +158,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..d7c9d9769330f --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs @@ -0,0 +1,13 @@ +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation +{ + 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 2a4be5758b7b6..a30f1b0f3bdbd 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionSet3.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/RoslynCompletionSet.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -12,16 +11,15 @@ using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; using VSCompletion = Microsoft.VisualStudio.Language.Intellisense.Completion; using Microsoft.CodeAnalysis.Snippets; 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(); @@ -30,11 +28,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) @@ -46,19 +50,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, @@ -77,12 +77,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); @@ -93,8 +95,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) { @@ -151,7 +153,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. @@ -194,6 +196,7 @@ private string GetLanguage() return ""; } +#if DEV15 private CompletionHelper GetCompletionHelper() { _foregroundObject.AssertIsForeground(); @@ -209,11 +212,7 @@ private CompletionHelper GetCompletionHelper() return _completionHelper; } -#if NEWCOMPLETION public override IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) -#else - public IReadOnlyList GetHighlightedSpansInDisplayText(string displayText) -#endif { if (_completionItemToFilterText != null) { @@ -242,7 +241,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..e0a0ffdeb6776 --- /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.VS2015)] + 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 01c79278432af..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Temporary/IIntellisenseFilter.cs +++ /dev/null @@ -1,47 +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 -{ - /// - /// 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/Next/EditorFeatures.Next.csproj b/src/EditorFeatures/Next/EditorFeatures.Next.csproj index 44343d919bf4e..402115a2fd66e 100644 --- a/src/EditorFeatures/Next/EditorFeatures.Next.csproj +++ b/src/EditorFeatures/Next/EditorFeatures.Next.csproj @@ -10,12 +10,13 @@ Debug AnyCPU - {366bbcdc-b05f-4677-9b5b-78ba816a1484} + {366BBCDC-B05F-4677-9B5B-78BA816A1484} Library Microsoft.CodeAnalysis.Editor Microsoft.CodeAnalysis.EditorFeatures.Next v4.6 false + DEV15 @@ -42,6 +43,10 @@ {EDC68A0E-C68D-4A74-91B7-BF38EC909888} Features + + {3cdeeab7-2256-418a-beb2-620b5cb16302} + EditorFeatures + {18F5FBB8-7570-4412-8CC7-0A86FF13B7BA} TextEditorFeatures @@ -68,7 +73,14 @@ + + + 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 88% rename from src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs rename to src/EditorFeatures/Next/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs index 1cb4f5590987a..8360e0320618f 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/IntellisenseFilter2.cs @@ -6,11 +6,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..89e2beba8e816 --- /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.VS15)] + 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/project.json b/src/EditorFeatures/Next/project.json index 718f042f58ddd..3d2e6fbd053de 100644 --- a/src/EditorFeatures/Next/project.json +++ b/src/EditorFeatures/Next/project.json @@ -1,5 +1,6 @@ { "dependencies": { + "Microsoft.VisualStudio.Language.Intellisense": "15.0.25123-Dev15Preview", }, "frameworks": { "net46": {} diff --git a/src/EditorFeatures/Text/TextEditorFeatures.csproj b/src/EditorFeatures/Text/TextEditorFeatures.csproj index 672ef17847b29..598c97f273ce1 100644 --- a/src/EditorFeatures/Text/TextEditorFeatures.csproj +++ b/src/EditorFeatures/Text/TextEditorFeatures.csproj @@ -40,6 +40,7 @@ + diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index a40cf33bdcc1d..5608c5ec2f409 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -34,6 +34,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..bce84f27ac607 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.Select(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) From 87cdf6633ed22d94c581120250fdaf1d89efe149 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Sat, 23 Apr 2016 20:09:11 -0700 Subject: [PATCH 18/28] Add a VS "15" specific VSIX. --- Roslyn.sln | 33 ++++++++-- .../Setup.Next/AssemblyRedirects.cs | 14 +++++ .../Setup.Next/VisualStudioSetup.Next.csproj | 62 +++++++++++++++++++ src/VisualStudio/Setup.Next/project.json | 11 ++++ .../Setup.Next/source.extension.vsixmanifest | 20 ++++++ .../Setup/VisualStudioSetup.csproj | 3 +- 6 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 src/VisualStudio/Setup.Next/AssemblyRedirects.cs create mode 100644 src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj create mode 100644 src/VisualStudio/Setup.Next/project.json create mode 100644 src/VisualStudio/Setup.Next/source.extension.vsixmanifest diff --git a/Roslyn.sln b/Roslyn.sln index 3305b8dac1c8c..924423c8aba59 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -365,6 +365,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Roslyn", "src\Deployment\Ro 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 @@ -387,28 +389,28 @@ Global src\Compilers\Core\CommandLine\CommandLine.projitems*{ad6f474e-e6d4-4217-91f3-b7af1be31ccc}*SharedItemsImports = 13 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 - src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 src\Compilers\Server\ServerShared\ServerShared.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 + src\Compilers\Core\CommandLine\CommandLine.projitems*{06b26dcb-7a12-48ef-ae50-708593abd05f}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 4 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 4 - src\Compilers\Core\CommandLine\CommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 src\Compilers\Server\ServerShared\ServerShared.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 + src\Compilers\Core\CommandLine\CommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 4 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 4 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\CommandLine\CommandLine.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*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 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bedc5a4a-809e-4017-9cfd-6c8d4e1847f0}*SharedItemsImports = 4 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 4 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 4 src\Compilers\Core\SharedCollections\SharedCollections.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13 - src\Compilers\Core\CommandLine\CommandLine.projitems*{d874349c-8bb3-4bdc-8535-2d52ccca1198}*SharedItemsImports = 4 src\Compilers\Core\MSBuildTask\Shared\MSBuildTask.Shared.projitems*{d874349c-8bb3-4bdc-8535-2d52ccca1198}*SharedItemsImports = 4 + src\Compilers\Core\CommandLine\CommandLine.projitems*{d874349c-8bb3-4bdc-8535-2d52ccca1198}*SharedItemsImports = 4 src\Compilers\Core\CommandLine\CommandLine.projitems*{e3cd2895-76a8-4d11-a316-ea67cb5ea42c}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -3152,6 +3154,26 @@ Global {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 @@ -3319,5 +3341,6 @@ Global {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/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..9324d8b8250f6 --- /dev/null +++ b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj @@ -0,0 +1,62 @@ + + + + + + + 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 + + + + {366BBCDC-B05F-4677-9B5B-78BA816A1484} + EditorFeatures.Next + BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b + DebugSymbolsProjectOutputGroup%3b + + + VisualStudioSetup + + + + AnyCPU + + + AnyCPU + + + Program + $(DevEnvDir)devenv.exe + /rootsuffix RoslynDev /log + + + + + Designer + + + + + + + + + + + + \ No newline at end of file diff --git a/src/VisualStudio/Setup.Next/project.json b/src/VisualStudio/Setup.Next/project.json new file mode 100644 index 0000000000000..2a1dd2816adc8 --- /dev/null +++ b/src/VisualStudio/Setup.Next/project.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + }, + "frameworks": { + "net46": { } + }, + "runtimes": { + "win7": { }, + "win7-anycpu": { } + } +} diff --git a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest new file mode 100644 index 0000000000000..96a087ecd1841 --- /dev/null +++ b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest @@ -0,0 +1,20 @@ + + + + + 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 ad6cb751c5f3a..f32f815c50527 100644 --- a/src/VisualStudio/Setup/VisualStudioSetup.csproj +++ b/src/VisualStudio/Setup/VisualStudioSetup.csproj @@ -140,6 +140,7 @@ + @@ -181,4 +182,4 @@ - \ No newline at end of file + From 7cef3b45c6eee054b7e53176d6b6ccd1a2b24e0f Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Sat, 23 Apr 2016 19:22:44 -0700 Subject: [PATCH 19/28] Remove SuppressionStateColumnFilterDefinition since the Shell has it. --- .../SuppressionStateColumnFilterDefinition.cs | 23 ------------------- .../Core/Def/ServicesVisualStudio.csproj | 3 +-- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/SuppressionStateColumnFilterDefinition.cs 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 37868ecf19dd8..0000000000000 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/SuppressionStateColumnFilterDefinition.cs +++ /dev/null @@ -1,23 +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 -{ - /// - /// 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 db5fbbc8a8e2f..cfc34cd62512a 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -107,7 +107,6 @@ - @@ -739,4 +738,4 @@ - + \ No newline at end of file From 9f8c179cc870f227944160ac2fefeb5462fd1706 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:14:20 -0700 Subject: [PATCH 20/28] Extract common code to a helper method --- .../Core/MSBuildTaskTests/IntegrationTests.cs | 17 ++------------ .../VBCSCompilerTests/CompilerServerTests.cs | 22 +------------------ src/Test/Utilities/Desktop/TestHelpers.cs | 20 +++++++++++++++++ 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs index 0dff21e338b7d..f2d3d02a1a5e8 100644 --- a/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs @@ -16,8 +16,6 @@ using System.Xml; using System.Threading.Tasks; -using static System.FormattableString; - namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests { public class IntegrationTests : TestBase @@ -27,19 +25,8 @@ public class IntegrationTests : TestBase static IntegrationTests() { - var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; - using (var key = Registry.LocalMachine.OpenSubKey(Invariant($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}"), 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/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index f1d98684dcb3e..d1c64da2f2b72 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -22,8 +22,6 @@ using Microsoft.CodeAnalysis.CommandLine; using Moq; -using static System.FormattableString; - namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests { public class CompilerServerUnitTests : TestBase @@ -53,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; @@ -76,24 +74,6 @@ private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs e) return null; } - private static string GetMSBuildDirectory() - { - var vsVersion = Environment.GetEnvironmentVariable("VisualStudioVersion") ?? "14.0"; - using (var key = Registry.LocalMachine.OpenSubKey(Invariant($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{vsVersion}"), 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/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; + } + } } From cc3c715ac45041451506a332e441a50d349c4b2d Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:21:57 -0700 Subject: [PATCH 21/28] Rename and simplify method --- .../Core/Extensibility/VersionSelector.cs | 13 ++----------- .../Completion/Presentation/CompletionPresenter.cs | 2 +- .../Completion/Presentation/CompletionPresenter.cs | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/VersionSelector.cs b/src/EditorFeatures/Core/Extensibility/VersionSelector.cs index 48e41cae5e83e..0dc2c7000004d 100644 --- a/src/EditorFeatures/Core/Extensibility/VersionSelector.cs +++ b/src/EditorFeatures/Core/Extensibility/VersionSelector.cs @@ -9,18 +9,9 @@ namespace Microsoft.CodeAnalysis.Editor { internal sealed class VersionSelector { - public static T Select(IEnumerable> items) + public static T SelectHighest(IEnumerable> items) { - var best = items.FirstOrDefault(); - foreach (var item in items.Skip(1)) - { - if ((int)item.Metadata.Version > (int)best.Metadata.Version) - { - best = item; - } - } - - return best.Value; + 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 97d465535af16..38156510624e1 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/CompletionPresenter.cs @@ -30,7 +30,7 @@ internal sealed class CompletionPresenter : ForegroundThreadAffinitizedObject, I { _completionBroker = completionBroker; _glyphService = glyphService; - _completionSetFactory = VersionSelector.Select(completionSetFactories); + _completionSetFactory = VersionSelector.SelectHighest(completionSetFactories); } ICompletionPresenterSession IIntelliSensePresenter.CreateSession(ITextView textView, ITextBuffer subjectBuffer, ICompletionSession session) diff --git a/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs b/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs index bce84f27ac607..eccbabf03deb7 100644 --- a/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs +++ b/src/Interactive/EditorFeatures/Core/Implementation/Completion/Presentation/CompletionPresenter.cs @@ -32,7 +32,7 @@ internal partial class CompletionPresenter : ForegroundThreadAffinitizedObject, { _completionBroker = completionBroker; _glyphService = glyphService; - _completionSetFactory = VersionSelector.Select(completionSetFactories); + _completionSetFactory = VersionSelector.SelectHighest(completionSetFactories); } ICompletionPresenterSession IIntelliSensePresenter.CreateSession(ITextView textView, ITextBuffer subjectBuffer, ICompletionSession sessionOpt) From d47605b6dd738ab47b1124822baf4e5da44c900f Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:22:13 -0700 Subject: [PATCH 22/28] Add doc comment --- .../Completion/Presentation/ICompletionSetFactory.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs index d7c9d9769330f..f2daef0ee25cf 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/ICompletionSetFactory.cs @@ -3,6 +3,11 @@ 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( From 5e13a909feb10d4647e2afb1d6819e9700f618f7 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:27:38 -0700 Subject: [PATCH 23/28] Rename enum values, and add doc comments --- .../Extensibility/ExportVersionSpecificAttribute.cs | 10 ++++++++-- .../Presentation/VisualStudio14CompletionSetFactory.cs | 2 +- .../Presentation/VisualStudio15CompletionSetFactory.cs | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs b/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs index 41617709dca12..e17b76e9733f4 100644 --- a/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs +++ b/src/EditorFeatures/Core/Extensibility/ExportVersionSpecificAttribute.cs @@ -7,10 +7,16 @@ namespace Microsoft.CodeAnalysis.Editor { + /// + /// Note: This need to be in ascending order, since we compare values in + /// . + /// internal enum VisualStudioVersion { - VS2015 = 14, - VS15 = 15, + /// VS Version 14, aka 'VS 2015' + Dev14 = 14, + /// VS Version 15, aka 'VS "15"' + Dev15 = 15, } [MetadataAttribute] diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs index e0a0ffdeb6776..db6b7562db0d3 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Presentation/VisualStudio14CompletionSetFactory.cs @@ -4,7 +4,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation { - [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.VS2015)] + [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.Dev14)] internal sealed class VisualStudio14CompletionSetFactory : ICompletionSetFactory { public ICompletionSet CreateCompletionSet( diff --git a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs index 89e2beba8e816..cc21608cbebe6 100644 --- a/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs +++ b/src/EditorFeatures/Next/IntelliSense/Completion/Presentation/VisualStudio15CompletionSetFactory.cs @@ -3,7 +3,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.Presentation { - [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.VS15)] + [ExportVersionSpecific(typeof(ICompletionSetFactory), VisualStudioVersion.Dev15)] internal sealed class VisualStudio15CompletionSetFactory : ICompletionSetFactory { public ICompletionSet CreateCompletionSet( From e603ddae3fceb4688297cc4f15d6d9e810cc003a Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:32:09 -0700 Subject: [PATCH 24/28] Remove copy and pasted cruft --- src/EditorFeatures/Next/EditorFeatures.Next.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/Next/EditorFeatures.Next.csproj b/src/EditorFeatures/Next/EditorFeatures.Next.csproj index 402115a2fd66e..eacb774c7ea8b 100644 --- a/src/EditorFeatures/Next/EditorFeatures.Next.csproj +++ b/src/EditorFeatures/Next/EditorFeatures.Next.csproj @@ -2,7 +2,6 @@ CSharp - true From 44a7358c6727b955b30eea3be9c9a0cbfe2c2077 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:32:27 -0700 Subject: [PATCH 25/28] Remove no longer needed runtime --- src/VisualStudio/Setup.Next/project.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/VisualStudio/Setup.Next/project.json b/src/VisualStudio/Setup.Next/project.json index 2a1dd2816adc8..0b16291820b67 100644 --- a/src/VisualStudio/Setup.Next/project.json +++ b/src/VisualStudio/Setup.Next/project.json @@ -5,7 +5,6 @@ "net46": { } }, "runtimes": { - "win7": { }, - "win7-anycpu": { } + "win7": { } } } From bc58ac22d302dfb235d408e0c1252ae674917af6 Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:47:38 -0700 Subject: [PATCH 26/28] Remove commented out stuff --- .../Core/Test/ServicesVisualStudioTest.vbproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index e5404396deecb..78b66253c9f0c 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -145,16 +145,6 @@ - From 778dfd61d5b0bd001cf8880f4ac16425f97abfec Mon Sep 17 00:00:00 2001 From: Kevin Pilch-Bisson Date: Thu, 12 May 2016 20:55:16 -0700 Subject: [PATCH 27/28] Cleanup vsix dependencies --- .../Setup.Next/VisualStudioSetup.Next.csproj | 13 +++++++++++-- .../Setup.Next/source.extension.vsixmanifest | 1 + src/VisualStudio/Setup/VisualStudioSetup.csproj | 4 ---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj index 9324d8b8250f6..db8a237254fc3 100644 --- a/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj +++ b/src/VisualStudio/Setup.Next/VisualStudioSetup.Next.csproj @@ -23,14 +23,20 @@ true + + {8da861d8-0cce-4334-b6c0-01a846c881ec} + VisualStudio + False + {366BBCDC-B05F-4677-9B5B-78BA816A1484} EditorFeatures.Next - BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b + BuiltProjectOutputGroup%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b DebugSymbolsProjectOutputGroup%3b VisualStudioSetup + false @@ -54,9 +60,12 @@ + + ProvideRoslynBindingRedirection.cs + - \ No newline at end of file + diff --git a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest index 96a087ecd1841..02a77efbd27d3 100644 --- a/src/VisualStudio/Setup.Next/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup.Next/source.extension.vsixmanifest @@ -13,6 +13,7 @@ + diff --git a/src/VisualStudio/Setup/VisualStudioSetup.csproj b/src/VisualStudio/Setup/VisualStudioSetup.csproj index f32f815c50527..d059eb26b6abc 100644 --- a/src/VisualStudio/Setup/VisualStudioSetup.csproj +++ b/src/VisualStudio/Setup/VisualStudioSetup.csproj @@ -138,10 +138,6 @@ true - - - - From 701404c794f32ae794f722aec26299586d2daab2 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Fri, 13 May 2016 21:09:24 +0200 Subject: [PATCH 28/28] Add InternalImplementationOnly and doc-comments on IOperation (#11246) This supports running an analyzer that warns when a user implements these interfaces. A prerequisite to fix https://github.com/dotnet/roslyn-analyzers/issues/863. - Copied the remark from `ISymbol` derivations on all public interfaces that derive from `IOperation` - Added remark and `[InternalImplementationOnly]` on `IOperation` - Removed `[InternalImplementationOnly]` from `ISourceAssemblySymbol` because it was only there, unneeded --- .../Core/Portable/Compilation/IExpression.cs | 207 +++++++++++++++++- .../Core/Portable/Compilation/IOperation.cs | 6 + .../Core/Portable/Compilation/IStatement.cs | 124 ++++++++++- .../Portable/Symbols/ISourceAssemblySymbol.cs | 1 - 4 files changed, 331 insertions(+), 7 deletions(-) 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 cf2b14b1a5650..6855b081bf3e4 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; }