未验证 提交 6d9992eb 编写于 作者: C Charles Stoner 提交者: GitHub

Merge pull request #23757 from dotnet/merges/master-to-features/compiler-20171213-080026

Merge master to features/compiler
...@@ -122,6 +122,13 @@ then ...@@ -122,6 +122,13 @@ then
build_args+=" /p:BootstrapBuildPath=${bootstrap_path}" build_args+=" /p:BootstrapBuildPath=${bootstrap_path}"
fi fi
# https://github.com/dotnet/roslyn/issues/23736
UNAME="$(uname)"
if [[ "$UNAME" == "Darwin" ]]
then
build_args+=" /p:UseRoslynAnalyzers=false"
fi
if [[ "${build}" == true ]] if [[ "${build}" == true ]]
then then
echo "Building Compilers.sln" echo "Building Compilers.sln"
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<!-- The release moniker for our packages. Developers should use "dev" and official builds pick the branch <!-- The release moniker for our packages. Developers should use "dev" and official builds pick the branch
moniker listed below --> moniker listed below -->
<RoslynNuGetMoniker Condition="'$(RoslynNuGetMoniker)' == ''">dev</RoslynNuGetMoniker> <RoslynNuGetMoniker Condition="'$(RoslynNuGetMoniker)' == ''">dev</RoslynNuGetMoniker>
<RoslynNuGetMoniker Condition="'$(OfficialBuild)' == 'true'">beta2</RoslynNuGetMoniker> <RoslynNuGetMoniker Condition="'$(OfficialBuild)' == 'true'">beta3</RoslynNuGetMoniker>
<!-- This is the base of the NuGet versioning for prerelease packages --> <!-- This is the base of the NuGet versioning for prerelease packages -->
<NuGetPreReleaseVersion>$(RoslynFileVersionBase)-$(RoslynNuGetMoniker)</NuGetPreReleaseVersion> <NuGetPreReleaseVersion>$(RoslynFileVersionBase)-$(RoslynNuGetMoniker)</NuGetPreReleaseVersion>
......
...@@ -5,7 +5,14 @@ ...@@ -5,7 +5,14 @@
"version": "2.7.*", "version": "2.7.*",
"nuget": [ "https://dotnet.myget.org/F/roslyn/api/v2/package" ], "nuget": [ "https://dotnet.myget.org/F/roslyn/api/v2/package" ],
"vsix": [ "https://dotnet.myget.org/F/roslyn/vsix/upload" ], "vsix": [ "https://dotnet.myget.org/F/roslyn/vsix/upload" ],
"channels": [ "dev15.6" ] "channels": [ "dev15.6", "dev15.6p3" ]
},
"dev15.6-preview2-vs-deps": {
"nugetKind": "PerBuildPreRelease",
"version": "2.7.*",
"nuget": [ "https://dotnet.myget.org/F/roslyn/api/v2/package" ],
"vsix": [ "https://dotnet.myget.org/F/roslyn/vsix/upload" ],
"channels": [ "dev15.6p2" ]
}, },
"dev15.5.x": { "dev15.5.x": {
"nugetKind": "PerBuildPreRelease", "nugetKind": "PerBuildPreRelease",
......
...@@ -45,8 +45,8 @@ do ...@@ -45,8 +45,8 @@ do
runtimeconfig_json="${file_name%.*}".runtimeconfig.json runtimeconfig_json="${file_name%.*}".runtimeconfig.json
# If the user specifies a test on the command line, only run that one # If the user specifies a test on the command line, only run that one
# "${2:-}" => take second arg, empty string if unset (regex always matches if empty) # "${2:-}" => take second arg, empty string if unset
if [[ ! "${file_name}" =~ "${2:-}" ]] if [[ ("${2:-}" != "") && (! "${file_name}" =~ "${2:-}") ]]
then then
echo "Skipping ${file_name}" echo "Skipping ${file_name}"
continue continue
......
...@@ -305,14 +305,14 @@ internal virtual ImportChain ImportChain ...@@ -305,14 +305,14 @@ internal virtual ImportChain ImportChain
} }
/// <summary> /// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly /// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder. /// check for certain attribute applications in context of this binder.
/// </summary> /// </summary>
internal virtual QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker internal virtual QuickAttributeChecker QuickAttributeChecker
{ {
get get
{ {
return _next.QuickTypeIdentifierAttributeChecker; return _next.QuickAttributeChecker;
} }
} }
......
...@@ -28,14 +28,14 @@ internal override ImportChain ImportChain ...@@ -28,14 +28,14 @@ internal override ImportChain ImportChain
} }
/// <summary> /// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly /// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder. /// check for certain attribute applications in context of this binder.
/// </summary> /// </summary>
internal override QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker internal override QuickAttributeChecker QuickAttributeChecker
{ {
get get
{ {
return QuickTypeIdentifierAttributeChecker.Predefined; return QuickAttributeChecker.Predefined;
} }
} }
......
...@@ -21,7 +21,8 @@ internal sealed class InContainerBinder : Binder ...@@ -21,7 +21,8 @@ internal sealed class InContainerBinder : Binder
private readonly Func<ConsList<Symbol>, Imports> _computeImports; private readonly Func<ConsList<Symbol>, Imports> _computeImports;
private Imports _lazyImports; private Imports _lazyImports;
private ImportChain _lazyImportChain; private ImportChain _lazyImportChain;
private QuickTypeIdentifierAttributeChecker _lazyQuickTypeIdentifierAttributeChecker; private QuickAttributeChecker _lazyQuickAttributeChecker;
private readonly SyntaxList<UsingDirectiveSyntax> _usingsSyntax;
/// <summary> /// <summary>
/// Creates a binder for a container with imports (usings and extern aliases) that can be /// Creates a binder for a container with imports (usings and extern aliases) that can be
...@@ -35,6 +36,20 @@ internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpS ...@@ -35,6 +36,20 @@ internal InContainerBinder(NamespaceOrTypeSymbol container, Binder next, CSharpS
_container = container; _container = container;
_computeImports = basesBeingResolved => Imports.FromSyntax(declarationSyntax, this, basesBeingResolved, inUsing); _computeImports = basesBeingResolved => Imports.FromSyntax(declarationSyntax, this, basesBeingResolved, inUsing);
if (!inUsing)
{
if (declarationSyntax.Kind() == SyntaxKind.CompilationUnit)
{
var compilationUnit = (CompilationUnitSyntax)declarationSyntax;
_usingsSyntax = compilationUnit.Usings;
}
else if (declarationSyntax.Kind() == SyntaxKind.NamespaceDeclaration)
{
var namespaceDecl = (NamespaceDeclarationSyntax)declarationSyntax;
_usingsSyntax = namespaceDecl.Usings;
}
}
} }
/// <summary> /// <summary>
...@@ -103,26 +118,26 @@ internal override ImportChain ImportChain ...@@ -103,26 +118,26 @@ internal override ImportChain ImportChain
} }
/// <summary> /// <summary>
/// Get <see cref="QuickTypeIdentifierAttributeChecker"/> that can be used to quickly /// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for TypeIdentifier attribute applications in context of this binder. /// check for certain attribute applications in context of this binder.
/// </summary> /// </summary>
internal override QuickTypeIdentifierAttributeChecker QuickTypeIdentifierAttributeChecker internal override QuickAttributeChecker QuickAttributeChecker
{ {
get get
{ {
if (_lazyQuickTypeIdentifierAttributeChecker == null) if (_lazyQuickAttributeChecker == null)
{ {
QuickTypeIdentifierAttributeChecker result = this.Next.QuickTypeIdentifierAttributeChecker; QuickAttributeChecker result = this.Next.QuickAttributeChecker;
if ((object)_container == null || _container.Kind == SymbolKind.Namespace) if ((object)_container == null || _container.Kind == SymbolKind.Namespace)
{ {
result = result.AddAliasesIfAny(GetImports(basesBeingResolved: null).UsingAliases); result = result.AddAliasesIfAny(_usingsSyntax);
} }
_lazyQuickTypeIdentifierAttributeChecker = result; _lazyQuickAttributeChecker = result;
} }
return _lazyQuickTypeIdentifierAttributeChecker; return _lazyQuickAttributeChecker;
} }
} }
......
// 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.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// The QuickAttributeChecker applies a simple fast heuristic for determining probable
/// attributes of certain kinds without binding attribute types, just by looking at the final syntax of an
/// attribute usage.
/// </summary>
/// <remarks>
/// It works by maintaining a dictionary of all possible simple names that might map to the given
/// attribute.
/// </remarks>
internal sealed class QuickAttributeChecker
{
private readonly Dictionary<string, QuickAttributes> _nameToAttributeMap;
private static QuickAttributeChecker _lazyPredefinedQuickAttributeChecker;
#if DEBUG
private bool _sealed;
#endif
internal static QuickAttributeChecker Predefined
{
get
{
if (_lazyPredefinedQuickAttributeChecker is null)
{
Interlocked.CompareExchange(ref _lazyPredefinedQuickAttributeChecker, CreatePredefinedQuickAttributeChecker(), null);
}
return _lazyPredefinedQuickAttributeChecker;
}
}
private static QuickAttributeChecker CreatePredefinedQuickAttributeChecker()
{
var result = new QuickAttributeChecker();
result.AddName(AttributeDescription.TypeIdentifierAttribute.Name, QuickAttributes.TypeIdentifier);
result.AddName(AttributeDescription.TypeForwardedToAttribute.Name, QuickAttributes.TypeForwardedTo);
#if DEBUG
result._sealed = true;
#endif
return result;
}
private QuickAttributeChecker()
{
_nameToAttributeMap = new Dictionary<string, QuickAttributes>(StringComparer.Ordinal);
// NOTE: caller must seal
}
private QuickAttributeChecker(QuickAttributeChecker previous)
{
_nameToAttributeMap = new Dictionary<string, QuickAttributes>(previous._nameToAttributeMap, StringComparer.Ordinal);
// NOTE: caller must seal
}
private void AddName(string name, QuickAttributes newAttributes)
{
#if DEBUG
Debug.Assert(!_sealed);
#endif
var currentValue = QuickAttributes.None;
_nameToAttributeMap.TryGetValue(name, out currentValue);
QuickAttributes newValue = newAttributes | currentValue;
_nameToAttributeMap[name] = newValue;
}
internal QuickAttributeChecker AddAliasesIfAny(SyntaxList<UsingDirectiveSyntax> usingsSyntax)
{
if (usingsSyntax.Count == 0)
{
return this;
}
QuickAttributeChecker newChecker = null;
foreach (var usingDirective in usingsSyntax)
{
if (usingDirective.Alias != null)
{
string name = usingDirective.Alias.Name.Identifier.ValueText;
string target = usingDirective.Name.GetUnqualifiedName().Identifier.ValueText;
if (_nameToAttributeMap.TryGetValue(target, out var foundAttributes))
{
// copy the QuickAttributes from alias target to alias name
(newChecker ?? (newChecker = new QuickAttributeChecker(this))).AddName(name, foundAttributes);
}
}
}
if (newChecker != null)
{
#if DEBUG
newChecker._sealed = true;
#endif
return newChecker;
}
return this;
}
public bool IsPossibleMatch(AttributeSyntax attr, QuickAttributes pattern)
{
#if DEBUG
Debug.Assert(_sealed);
#endif
string name = attr.Name.GetUnqualifiedName().Identifier.ValueText;
QuickAttributes foundAttributes;
// We allow "Name" to bind to "NameAttribute"
if (_nameToAttributeMap.TryGetValue(name, out foundAttributes) ||
_nameToAttributeMap.TryGetValue(name + "Attribute", out foundAttributes))
{
return (foundAttributes & pattern) != 0;
}
return false;
}
}
[Flags]
internal enum QuickAttributes : byte
{
None = 0,
TypeIdentifier = 1 << 0,
TypeForwardedTo = 2 << 0
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// The QuickTypeIdentifierAttributeChecker applies a simple fast heuristic for determining probable
/// TypeIdentifier attributes without binding attribute types, just by looking at the final syntax of an
/// attribute usage. It is accessed via the QuickTypeIdentifierAttributeChecker property on Binder.
/// </summary>
/// <remarks>
/// It works by maintaining a dictionary of all possible simple names that might map to a TypeIdentifier
/// attribute.
/// </remarks>
internal class QuickTypeIdentifierAttributeChecker
{
private readonly HashSet<string> _candidates;
#if DEBUG
private bool _sealed;
#endif
public static readonly QuickTypeIdentifierAttributeChecker Predefined = new QuickTypeIdentifierAttributeChecker();
private QuickTypeIdentifierAttributeChecker()
{
_candidates = new HashSet<string>();
_candidates.Add(AttributeDescription.TypeIdentifierAttribute.Name);
#if DEBUG
_sealed = true;
#endif
}
private QuickTypeIdentifierAttributeChecker(QuickTypeIdentifierAttributeChecker previous)
{
_candidates = new HashSet<string>(previous._candidates);
}
private void AddCandidate(string candidate)
{
#if DEBUG
Debug.Assert(!_sealed);
#endif
_candidates.Add(candidate);
}
public QuickTypeIdentifierAttributeChecker AddAliasesIfAny(ImmutableDictionary<string, AliasAndUsingDirective> usingAliases)
{
QuickTypeIdentifierAttributeChecker newChecker = null;
foreach (KeyValuePair<string, AliasAndUsingDirective> pair in usingAliases)
{
if (_candidates.Contains(pair.Value.UsingDirective.Name.GetUnqualifiedName().Identifier.ValueText))
{
(newChecker ?? (newChecker = new QuickTypeIdentifierAttributeChecker(this))).AddCandidate(pair.Key);
}
}
if (newChecker != null)
{
#if DEBUG
newChecker._sealed = true;
#endif
return newChecker;
}
return this;
}
public bool IsPossibleMatch(AttributeSyntax attr)
{
#if DEBUG
Debug.Assert(_sealed);
#endif
string name = attr.Name.GetUnqualifiedName().Identifier.ValueText;
return _candidates.Contains(name) || _candidates.Contains(name + "Attribute");
}
}
}
...@@ -1556,6 +1556,33 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute ...@@ -1556,6 +1556,33 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute
return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; return (CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
} }
/// <summary>
/// This only forces binding of attributes that look like they may be forwarded types attributes (syntactically).
/// </summary>
internal HashSet<NamedTypeSymbol> GetForwardedTypes()
{
CustomAttributesBag<CSharpAttributeData> attributesBag = _lazySourceAttributesBag;
if (attributesBag?.IsDecodedWellKnownAttributeDataComputed == true)
{
// Use already decoded attributes
return ((CommonAssemblyWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData)?.ForwardedTypes;
}
attributesBag = null;
LoadAndValidateAttributes(OneOrMany.Create(GetAttributeDeclarations()), ref attributesBag, attributeMatchesOpt: this.IsPossibleForwardedTypesAttribute);
var wellKnownAttributeData = (CommonAssemblyWellKnownAttributeData)attributesBag?.DecodedWellKnownAttributeData;
return wellKnownAttributeData?.ForwardedTypes;
}
private bool IsPossibleForwardedTypesAttribute(AttributeSyntax node)
{
QuickAttributeChecker checker =
this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker;
return checker.IsPossibleMatch(node, QuickAttributes.TypeForwardedTo);
}
private static IEnumerable<Cci.SecurityAttribute> GetSecurityAttributes(CustomAttributesBag<CSharpAttributeData> attributesBag) private static IEnumerable<Cci.SecurityAttribute> GetSecurityAttributes(CustomAttributesBag<CSharpAttributeData> attributesBag)
{ {
Debug.Assert(attributesBag.IsSealed); Debug.Assert(attributesBag.IsSealed);
...@@ -2556,13 +2583,14 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti ...@@ -2556,13 +2583,14 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti
if (_lazyForwardedTypesFromSource == null) if (_lazyForwardedTypesFromSource == null)
{ {
IDictionary<string, NamedTypeSymbol> forwardedTypesFromSource; IDictionary<string, NamedTypeSymbol> forwardedTypesFromSource;
CommonAssemblyWellKnownAttributeData<NamedTypeSymbol> wellKnownAttributeData = GetSourceDecodedWellKnownAttributeData(); // Get the TypeForwardedTo attributes with minimal binding to avoid cycle problems
HashSet<NamedTypeSymbol> forwardedTypes = GetForwardedTypes();
if (wellKnownAttributeData != null && wellKnownAttributeData.ForwardedTypes != null) if (forwardedTypes != null)
{ {
forwardedTypesFromSource = new Dictionary<string, NamedTypeSymbol>(StringOrdinalComparer.Instance); forwardedTypesFromSource = new Dictionary<string, NamedTypeSymbol>(StringOrdinalComparer.Instance);
foreach (NamedTypeSymbol forwardedType in wellKnownAttributeData.ForwardedTypes) foreach (NamedTypeSymbol forwardedType in forwardedTypes)
{ {
NamedTypeSymbol originalDefinition = forwardedType.OriginalDefinition; NamedTypeSymbol originalDefinition = forwardedType.OriginalDefinition;
Debug.Assert((object)originalDefinition.ContainingType == null, "How did a nested type get forwarded?"); Debug.Assert((object)originalDefinition.ContainingType == null, "How did a nested type get forwarded?");
......
...@@ -780,13 +780,13 @@ private void CheckPresenceOfTypeIdentifierAttribute() ...@@ -780,13 +780,13 @@ private void CheckPresenceOfTypeIdentifierAttribute()
foreach (SyntaxList<AttributeListSyntax> list in attributeLists) foreach (SyntaxList<AttributeListSyntax> list in attributeLists)
{ {
var syntaxTree = list.Node.SyntaxTree; var syntaxTree = list.Node.SyntaxTree;
QuickTypeIdentifierAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(list.Node.SyntaxTree).GetBinder(list.Node).QuickTypeIdentifierAttributeChecker; QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(list.Node.SyntaxTree).GetBinder(list.Node).QuickAttributeChecker;
foreach (AttributeListSyntax attrList in list) foreach (AttributeListSyntax attrList in list)
{ {
foreach (AttributeSyntax attr in attrList.Attributes) foreach (AttributeSyntax attr in attrList.Attributes)
{ {
if (checker.IsPossibleMatch(attr)) if (checker.IsPossibleMatch(attr, QuickAttributes.TypeIdentifier))
{ {
// This attribute syntax might be an application of TypeIdentifierAttribute. // This attribute syntax might be an application of TypeIdentifierAttribute.
// Let's bind it. // Let's bind it.
......
...@@ -255,19 +255,21 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu ...@@ -255,19 +255,21 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
/// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param> /// <param name="symbolPart">Specific part of the symbol to which the attributes apply, or <see cref="AttributeLocation.None"/> if the attributes apply to the symbol itself.</param>
/// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param> /// <param name="earlyDecodingOnly">Indicates that only early decoding should be performed. WARNING: the resulting bag will not be sealed.</param>
/// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param> /// <param name="binderOpt">Binder to use. If null, <see cref="DeclaringCompilation"/> GetBinderFactory will be used.</param>
/// <param name="attributeMatchesOpt">If specified, only load attributes that match this predicate, and any diagnostics produced will be dropped.</param>
/// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns> /// <returns>Flag indicating whether lazyCustomAttributes were stored on this thread. Caller should check for this flag and perform NotePartComplete if true.</returns>
internal bool LoadAndValidateAttributes( internal bool LoadAndValidateAttributes(
OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists, OneOrMany<SyntaxList<AttributeListSyntax>> attributesSyntaxLists,
ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag, ref CustomAttributesBag<CSharpAttributeData> lazyCustomAttributesBag,
AttributeLocation symbolPart = AttributeLocation.None, AttributeLocation symbolPart = AttributeLocation.None,
bool earlyDecodingOnly = false, bool earlyDecodingOnly = false,
Binder binderOpt = null) Binder binderOpt = null,
Func<AttributeSyntax, bool> attributeMatchesOpt = null)
{ {
var diagnostics = DiagnosticBag.GetInstance(); var diagnostics = DiagnosticBag.GetInstance();
var compilation = this.DeclaringCompilation; var compilation = this.DeclaringCompilation;
ImmutableArray<Binder> binders; ImmutableArray<Binder> binders;
ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, binderOpt, out binders); ImmutableArray<AttributeSyntax> attributesToBind = this.GetAttributesToBind(attributesSyntaxLists, symbolPart, diagnostics, compilation, attributeMatchesOpt, binderOpt, out binders);
Debug.Assert(!attributesToBind.IsDefault); Debug.Assert(!attributesToBind.IsDefault);
ImmutableArray<CSharpAttributeData> boundAttributes; ImmutableArray<CSharpAttributeData> boundAttributes;
...@@ -344,8 +346,11 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu ...@@ -344,8 +346,11 @@ internal virtual void PostDecodeWellKnownAttributes(ImmutableArray<CSharpAttribu
bool lazyAttributesStoredOnThisThread = false; bool lazyAttributesStoredOnThisThread = false;
if (lazyCustomAttributesBag.SetAttributes(boundAttributes)) if (lazyCustomAttributesBag.SetAttributes(boundAttributes))
{ {
this.RecordPresenceOfBadAttributes(boundAttributes); if (attributeMatchesOpt is null)
AddDeclarationDiagnostics(diagnostics); {
this.RecordPresenceOfBadAttributes(boundAttributes);
AddDeclarationDiagnostics(diagnostics);
}
lazyAttributesStoredOnThisThread = true; lazyAttributesStoredOnThisThread = true;
if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty; if (lazyCustomAttributesBag.IsEmpty) lazyCustomAttributesBag = CustomAttributesBag<CSharpAttributeData>.Empty;
} }
...@@ -382,6 +387,7 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> b ...@@ -382,6 +387,7 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> b
AttributeLocation symbolPart, AttributeLocation symbolPart,
DiagnosticBag diagnostics, DiagnosticBag diagnostics,
CSharpCompilation compilation, CSharpCompilation compilation,
Func<AttributeSyntax, bool> attributeMatchesOpt,
Binder rootBinderOpt, Binder rootBinderOpt,
out ImmutableArray<Binder> binders) out ImmutableArray<Binder> binders)
{ {
...@@ -409,8 +415,22 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> b ...@@ -409,8 +415,22 @@ private void RecordPresenceOfBadAttributes(ImmutableArray<CSharpAttributeData> b
} }
var attributesToBind = attributeDeclarationSyntax.Attributes; var attributesToBind = attributeDeclarationSyntax.Attributes;
syntaxBuilder.AddRange(attributesToBind); if (attributeMatchesOpt is null)
attributesToBindCount += attributesToBind.Count; {
syntaxBuilder.AddRange(attributesToBind);
attributesToBindCount += attributesToBind.Count;
}
else
{
foreach (var attribute in attributesToBind)
{
if (attributeMatchesOpt(attribute))
{
syntaxBuilder.Add(attribute);
attributesToBindCount++;
}
}
}
} }
} }
......
...@@ -4368,6 +4368,24 @@ public void PublicSign_KeyFileRelativePath() ...@@ -4368,6 +4368,24 @@ public void PublicSign_KeyFileRelativePath()
Assert.Equal(Path.Combine(_baseDirectory, "test.snk"), parsedArgs.CompilationOptions.CryptoKeyFile); Assert.Equal(Path.Combine(_baseDirectory, "test.snk"), parsedArgs.CompilationOptions.CryptoKeyFile);
} }
[Fact]
[WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
public void PublicSignWithEmptyKeyPath()
{
DefaultParse(new[] { "/publicsign", "/keyfile:", "a.cs" }, _baseDirectory).Errors.Verify(
// error CS2005: Missing file specification for 'keyfile' option
Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1));
}
[Fact]
[WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
public void PublicSignWithEmptyKeyPath2()
{
DefaultParse(new[] { "/publicsign", "/keyfile:\"\"", "a.cs" }, _baseDirectory).Errors.Verify(
// error CS2005: Missing file specification for 'keyfile' option
Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("keyfile").WithLocation(1, 1));
}
[WorkItem(546301, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546301")] [WorkItem(546301, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546301")]
[Fact] [Fact]
public void SubsystemVersionTests() public void SubsystemVersionTests()
......
...@@ -45,6 +45,26 @@ public void PublicSignWithRelativeKeyPath() ...@@ -45,6 +45,26 @@ public void PublicSignWithRelativeKeyPath()
); );
} }
[Fact]
[WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
public void PublicSignWithEmptyKeyPath()
{
CreateStandardCompilation("", options: TestOptions.ReleaseDll.WithPublicSign(true).WithCryptoKeyFile("")).VerifyDiagnostics(
// error CS8102: Public signing was specified and requires a public key, but no public key was specified.
Diagnostic(ErrorCode.ERR_PublicSignButNoKey).WithLocation(1, 1));
}
[Fact]
[WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")]
public void PublicSignWithEmptyKeyPath2()
{
CreateStandardCompilation("", options: TestOptions.ReleaseDll.WithPublicSign(true).WithCryptoKeyFile("\"\"")).VerifyDiagnostics(
// error CS8106: Option 'CryptoKeyFile' must be an absolute path.
Diagnostic(ErrorCode.ERR_OptionMustBeAbsolutePath).WithArguments("CryptoKeyFile").WithLocation(1, 1),
// error CS8102: Public signing was specified and requires a public key, but no public key was specified.
Diagnostic(ErrorCode.ERR_PublicSignButNoKey).WithLocation(1, 1));
}
[Fact] [Fact]
[WorkItem(233669, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=233669")] [WorkItem(233669, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=233669")]
public void CompilationName() public void CompilationName()
......
...@@ -8374,7 +8374,6 @@ End Class ...@@ -8374,7 +8374,6 @@ End Class
Assert.False(args.CompilationOptions.PublicSign) Assert.False(args.CompilationOptions.PublicSign)
End Sub End Sub
<WorkItem(8360, "https://github.com/dotnet/roslyn/issues/8360")> <WorkItem(8360, "https://github.com/dotnet/roslyn/issues/8360")>
<Fact> <Fact>
Public Sub PublicSign_KeyFileRelativePath() Public Sub PublicSign_KeyFileRelativePath()
...@@ -8383,6 +8382,20 @@ End Class ...@@ -8383,6 +8382,20 @@ End Class
parsedArgs.Errors.Verify() parsedArgs.Errors.Verify()
End Sub End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath()
Dim parsedArgs = FullParse("/publicsign /keyfile: a.cs", _baseDirectory)
parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":<file>").WithLocation(1, 1))
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath2()
Dim parsedArgs = FullParse("/publicsign /keyfile:"""" a.cs", _baseDirectory)
parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("keyfile", ":<file>").WithLocation(1, 1))
End Sub
<ConditionalFact(GetType(WindowsOnly))> <ConditionalFact(GetType(WindowsOnly))>
Public Sub CommandLineMisc() Public Sub CommandLineMisc()
Dim args As VisualBasicCommandLineArguments Dim args As VisualBasicCommandLineArguments
......
...@@ -34,6 +34,29 @@ BC37257: Option 'CryptoKeyFile' must be an absolute path. ...@@ -34,6 +34,29 @@ BC37257: Option 'CryptoKeyFile' must be an absolute path.
</errors>) </errors>)
End Sub End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath()
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).
WithPublicSign(True).WithCryptoKeyFile("")
AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options),
<errors>
BC37254: Public sign was specified and requires a public key, but no public key was specified
</errors>)
End Sub
<WorkItem(11497, "https://github.com/dotnet/roslyn/issues/11497")>
<Fact>
Public Sub PublicSignWithEmptyKeyPath2()
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).
WithPublicSign(True).WithCryptoKeyFile("""""")
AssertTheseDiagnostics(VisualBasicCompilation.Create("test", options:=options),
<errors>
BC37254: Public sign was specified and requires a public key, but no public key was specified
BC37257: Option 'CryptoKeyFile' must be an absolute path.
</errors>)
End Sub
<Fact> <Fact>
Public Sub LocalizableErrorArgumentToStringDoesntStackOverflow() Public Sub LocalizableErrorArgumentToStringDoesntStackOverflow()
' Error ID is arbitrary ' Error ID is arbitrary
......
...@@ -114,4 +114,7 @@ ...@@ -114,4 +114,7 @@
<InternalsVisibleToTest Include="Microsoft.VisualStudio.IntegrationTest.Utilities" /> <InternalsVisibleToTest Include="Microsoft.VisualStudio.IntegrationTest.Utilities" />
<InternalsVisibleToTest Include="Roslyn.VisualStudio.Test.Utilities2" /> <InternalsVisibleToTest Include="Roslyn.VisualStudio.Test.Utilities2" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project> </Project>
...@@ -33,7 +33,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem ...@@ -33,7 +33,9 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
using Workspace = Microsoft.CodeAnalysis.Workspace; using Workspace = Microsoft.CodeAnalysis.Workspace;
// NOTE: Microsoft.VisualStudio.LanguageServices.TypeScript.TypeScriptProject derives from AbstractProject. // NOTE: Microsoft.VisualStudio.LanguageServices.TypeScript.TypeScriptProject derives from AbstractProject.
#pragma warning disable CS0618 // IVisualStudioHostProject is obsolete
internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObject, IVisualStudioHostProject internal abstract partial class AbstractProject : ForegroundThreadAffinitizedObject, IVisualStudioHostProject
#pragma warning restore CS0618 // IVisualStudioHostProject is obsolete
{ {
internal static object RuleSetErrorId = new object(); internal static object RuleSetErrorId = new object();
private readonly object _gate = new object(); private readonly object _gate = new object();
...@@ -598,7 +600,7 @@ protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(s ...@@ -598,7 +600,7 @@ protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(s
// at this point, we don't know whether it is a metadata reference added because // at this point, we don't know whether it is a metadata reference added because
// we don't have enough information yet for p2p reference or user explicitly added it // we don't have enough information yet for p2p reference or user explicitly added it
// as a metadata reference. // as a metadata reference.
AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(this, filePath, properties)); AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(filePath, properties));
// here, we change behavior compared to old C# language service. regardless of file being exist or not, // here, we change behavior compared to old C# language service. regardless of file being exist or not,
// we will always return S_OK. this is to support cross language p2p reference better. // we will always return S_OK. this is to support cross language p2p reference better.
...@@ -1228,7 +1230,7 @@ internal void UndoProjectReferenceConversionForDisappearingOutputPath(string bin ...@@ -1228,7 +1230,7 @@ internal void UndoProjectReferenceConversionForDisappearingOutputPath(string bin
projectReference.Aliases, projectReference.Aliases,
projectReference.EmbedInteropTypes); projectReference.EmbedInteropTypes);
AddMetadataReferenceCore(MetadataReferenceProvider.CreateMetadataReference(this, binPath, metadataReferenceProperties)); AddMetadataReferenceCore(MetadataReferenceProvider.CreateMetadataReference(binPath, metadataReferenceProperties));
Contract.ThrowIfFalse(RemoveMetadataFileNameToConvertedProjectReference(binPath)); Contract.ThrowIfFalse(RemoveMetadataFileNameToConvertedProjectReference(binPath));
} }
...@@ -1255,7 +1257,7 @@ protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string ...@@ -1255,7 +1257,7 @@ protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string
RemoveMetadataReferenceCore(existingReference, disposeReference: true); RemoveMetadataReferenceCore(existingReference, disposeReference: true);
AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(this, file, newProperties)); AddMetadataReferenceCore(this.MetadataReferenceProvider.CreateMetadataReference(file, newProperties));
} }
} }
......
...@@ -16,13 +16,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem ...@@ -16,13 +16,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
/// </summary> /// </summary>
internal class DocumentKey : IEquatable<DocumentKey> internal class DocumentKey : IEquatable<DocumentKey>
{ {
private readonly IVisualStudioHostProject _hostProject; private readonly AbstractProject _hostProject;
private readonly string _moniker; private readonly string _moniker;
public IVisualStudioHostProject HostProject { get { return _hostProject; } } public AbstractProject HostProject { get { return _hostProject; } }
public string Moniker { get { return _moniker; } } public string Moniker { get { return _moniker; } }
public DocumentKey(IVisualStudioHostProject hostProject, string moniker) public DocumentKey(AbstractProject hostProject, string moniker)
{ {
Contract.ThrowIfNull(hostProject); Contract.ThrowIfNull(hostProject);
Contract.ThrowIfNull(moniker); Contract.ThrowIfNull(moniker);
......
...@@ -26,7 +26,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -26,7 +26,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
/// </summary> /// </summary>
private readonly DocumentProvider _documentProvider; private readonly DocumentProvider _documentProvider;
private readonly string _itemMoniker; private readonly string _itemMoniker;
private readonly ITextUndoHistoryRegistry _textUndoHistoryRegistry;
private readonly FileChangeTracker _fileChangeTracker; private readonly FileChangeTracker _fileChangeTracker;
private readonly ReiteratedVersionSnapshotTracker _snapshotTracker; private readonly ReiteratedVersionSnapshotTracker _snapshotTracker;
private readonly TextLoader _doNotAccessDirectlyLoader; private readonly TextLoader _doNotAccessDirectlyLoader;
...@@ -38,7 +37,7 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -38,7 +37,7 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
public DocumentId Id { get; } public DocumentId Id { get; }
public IReadOnlyList<string> Folders { get; } public IReadOnlyList<string> Folders { get; }
public IVisualStudioHostProject Project { get; } public AbstractProject Project { get; }
public SourceCodeKind SourceCodeKind { get; } public SourceCodeKind SourceCodeKind { get; }
public DocumentKey Key { get; } public DocumentKey Key { get; }
...@@ -52,11 +51,10 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -52,11 +51,10 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
/// </summary> /// </summary>
public StandardTextDocument( public StandardTextDocument(
DocumentProvider documentProvider, DocumentProvider documentProvider,
IVisualStudioHostProject project, AbstractProject project,
DocumentKey documentKey, DocumentKey documentKey,
Func<uint, IReadOnlyList<string>> getFolderNames, Func<uint, IReadOnlyList<string>> getFolderNames,
SourceCodeKind sourceCodeKind, SourceCodeKind sourceCodeKind,
ITextUndoHistoryRegistry textUndoHistoryRegistry,
IVsFileChangeEx fileChangeService, IVsFileChangeEx fileChangeService,
ITextBuffer openTextBuffer, ITextBuffer openTextBuffer,
DocumentId id, DocumentId id,
...@@ -79,7 +77,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS ...@@ -79,7 +77,6 @@ private class StandardTextDocument : ForegroundThreadAffinitizedObject, IVisualS
this.Key = documentKey; this.Key = documentKey;
this.SourceCodeKind = sourceCodeKind; this.SourceCodeKind = sourceCodeKind;
_textUndoHistoryRegistry = textUndoHistoryRegistry;
_fileChangeTracker = new FileChangeTracker(fileChangeService, this.FilePath); _fileChangeTracker = new FileChangeTracker(fileChangeService, this.FilePath);
_fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk; _fileChangeTracker.UpdatedOnDisk += OnUpdatedOnDisk;
...@@ -211,42 +208,20 @@ public void UpdateText(SourceText newText) ...@@ -211,42 +208,20 @@ public void UpdateText(SourceText newText)
// expensive source control check if the file is already checked out. // expensive source control check if the file is already checked out.
if (_openTextBuffer != null) if (_openTextBuffer != null)
{ {
UpdateText(newText, _openTextBuffer, EditOptions.DefaultMinimalChange); TextEditApplication.UpdateText(newText, _openTextBuffer, EditOptions.DefaultMinimalChange);
} }
else else
{ {
using (var invisibleEditor = ((VisualStudioWorkspaceImpl)this.Project.Workspace).OpenInvisibleEditor(this)) using (var invisibleEditor = ((VisualStudioWorkspaceImpl)this.Project.Workspace).OpenInvisibleEditor(this))
{ {
UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None); TextEditApplication.UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None);
} }
} }
} }
private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) public ITextBuffer GetTextUndoHistoryBuffer()
{ {
using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null)) return GetOpenTextBuffer();
{
var oldSnapshot = buffer.CurrentSnapshot;
var oldText = oldSnapshot.AsText();
var changes = newText.GetTextChanges(oldText);
if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace))
{
var undoService = workspace.Services.GetService<ISourceTextUndoService>();
undoService.BeginUndoTransaction(oldSnapshot);
}
foreach (var change in changes)
{
edit.Replace(change.Span.Start, change.Span.Length, change.NewText);
}
edit.ApplyAndLogExceptions();
}
}
public ITextUndoHistory GetTextUndoHistory()
{
return _textUndoHistoryRegistry.GetHistory(GetOpenTextBuffer());
} }
private string GetDebuggerDisplay() private string GetDebuggerDisplay()
......
...@@ -30,10 +30,9 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje ...@@ -30,10 +30,9 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje
#region Immutable readonly fields/properties that can be accessed from foreground or background threads - do not need locking for access. #region Immutable readonly fields/properties that can be accessed from foreground or background threads - do not need locking for access.
private readonly object _gate = new object(); private readonly object _gate = new object();
private readonly uint _runningDocumentTableEventCookie; private readonly uint _runningDocumentTableEventCookie;
private readonly IVisualStudioHostProjectContainer _projectContainer; private readonly VisualStudioProjectTracker _projectTracker;
private readonly IVsFileChangeEx _fileChangeService; private readonly IVsFileChangeEx _fileChangeService;
private readonly IVsTextManager _textManager; private readonly IVsTextManager _textManager;
private readonly ITextUndoHistoryRegistry _textUndoHistoryRegistry;
private readonly IVsRunningDocumentTable4 _runningDocumentTable; private readonly IVsRunningDocumentTable4 _runningDocumentTable;
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService;
private readonly IContentTypeRegistryService _contentTypeRegistryService; private readonly IContentTypeRegistryService _contentTypeRegistryService;
...@@ -54,22 +53,20 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje ...@@ -54,22 +53,20 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje
/// <summary> /// <summary>
/// Creates a document provider. /// Creates a document provider.
/// </summary> /// </summary>
/// <param name="projectContainer">Project container for the documents.</param>
/// <param name="serviceProvider">Service provider</param> /// <param name="serviceProvider">Service provider</param>
/// <param name="documentTrackingService">An optional <see cref="VisualStudioDocumentTrackingService"/> to track active and visible documents.</param> /// <param name="documentTrackingService">An optional <see cref="VisualStudioDocumentTrackingService"/> to track active and visible documents.</param>
public DocumentProvider( public DocumentProvider(
IVisualStudioHostProjectContainer projectContainer, VisualStudioProjectTracker projectTracker,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
VisualStudioDocumentTrackingService documentTrackingService) VisualStudioDocumentTrackingService documentTrackingService)
{ {
var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel)); var componentModel = (IComponentModel)serviceProvider.GetService(typeof(SComponentModel));
_projectContainer = projectContainer; _projectTracker = projectTracker;
this._documentTrackingServiceOpt = documentTrackingService; this._documentTrackingServiceOpt = documentTrackingService;
this._runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); this._runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
this._editorAdaptersFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>(); this._editorAdaptersFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
this._contentTypeRegistryService = componentModel.GetService<IContentTypeRegistryService>(); this._contentTypeRegistryService = componentModel.GetService<IContentTypeRegistryService>();
_textUndoHistoryRegistry = componentModel.GetService<ITextUndoHistoryRegistry>();
_textManager = (IVsTextManager)serviceProvider.GetService(typeof(SVsTextManager)); _textManager = (IVsTextManager)serviceProvider.GetService(typeof(SVsTextManager));
_fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx)); _fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx));
...@@ -85,6 +82,28 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje ...@@ -85,6 +82,28 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje
Marshal.ThrowExceptionForHR(runningDocumentTableForEvents.AdviseRunningDocTableEvents(new RunningDocTableEventsSink(this), out _runningDocumentTableEventCookie)); Marshal.ThrowExceptionForHR(runningDocumentTableForEvents.AdviseRunningDocTableEvents(new RunningDocTableEventsSink(this), out _runningDocumentTableEventCookie));
} }
[Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")]
public IVisualStudioHostDocument TryGetDocumentForFile(
IVisualStudioHostProject hostProject,
string filePath,
SourceCodeKind sourceCodeKind,
Func<ITextBuffer, bool> canUseTextBuffer,
Func<uint, IReadOnlyList<string>> getFolderNames,
EventHandler updatedOnDiskHandler = null,
EventHandler<bool> openedHandler = null,
EventHandler<bool> closingHandler = null)
{
return TryGetDocumentForFile(
(AbstractProject)hostProject,
filePath,
sourceCodeKind,
canUseTextBuffer,
getFolderNames,
updatedOnDiskHandler,
openedHandler,
closingHandler);
}
/// <summary> /// <summary>
/// Gets the <see cref="IVisualStudioHostDocument"/> for the file at the given filePath. /// Gets the <see cref="IVisualStudioHostDocument"/> for the file at the given filePath.
/// If we are on the foreground thread and this document is already open in the editor, /// If we are on the foreground thread and this document is already open in the editor,
...@@ -93,7 +112,7 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje ...@@ -93,7 +112,7 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje
/// whenever <see cref="NotifyDocumentRegisteredToProjectAndStartToRaiseEvents"/> is invoked for the returned document. /// whenever <see cref="NotifyDocumentRegisteredToProjectAndStartToRaiseEvents"/> is invoked for the returned document.
/// </summary> /// </summary>
public IVisualStudioHostDocument TryGetDocumentForFile( public IVisualStudioHostDocument TryGetDocumentForFile(
IVisualStudioHostProject hostProject, AbstractProject hostProject,
string filePath, string filePath,
SourceCodeKind sourceCodeKind, SourceCodeKind sourceCodeKind,
Func<ITextBuffer, bool> canUseTextBuffer, Func<ITextBuffer, bool> canUseTextBuffer,
...@@ -163,7 +182,6 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje ...@@ -163,7 +182,6 @@ internal sealed partial class DocumentProvider : ForegroundThreadAffinitizedObje
documentKey, documentKey,
getFolderNames, getFolderNames,
sourceCodeKind, sourceCodeKind,
_textUndoHistoryRegistry,
_fileChangeService, _fileChangeService,
openTextBuffer, openTextBuffer,
id, id,
...@@ -334,7 +352,7 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie) ...@@ -334,7 +352,7 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie)
if (_runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer shimTextBuffer) if (_runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer shimTextBuffer)
{ {
var hasAssociatedRoslynDocument = false; var hasAssociatedRoslynDocument = false;
foreach (var project in _projectContainer.GetProjects()) foreach (var project in _projectTracker.ImmutableProjects)
{ {
var documentKey = new DocumentKey(project, moniker); var documentKey = new DocumentKey(project, moniker);
...@@ -391,11 +409,11 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie) ...@@ -391,11 +409,11 @@ private void TryProcessOpenForDocCookie_NoLock(uint docCookie)
{ {
// This is opening some other designer or property page. If it's tied to our IVsHierarchy, we should // This is opening some other designer or property page. If it's tied to our IVsHierarchy, we should
// let the workspace know // let the workspace know
foreach (var project in _projectContainer.GetProjects()) foreach (var project in _projectTracker.ImmutableProjects)
{ {
if (hierarchy == project.Hierarchy) if (hierarchy == project.Hierarchy)
{ {
_projectContainer.NotifyNonDocumentOpenedForProject(project); _projectTracker.NotifyNonDocumentOpenedForProject(project);
} }
} }
} }
......
...@@ -22,7 +22,7 @@ internal interface IVisualStudioHostDocument : IDisposable ...@@ -22,7 +22,7 @@ internal interface IVisualStudioHostDocument : IDisposable
/// <summary> /// <summary>
/// The visual studio project this document is part of. /// The visual studio project this document is part of.
/// </summary> /// </summary>
IVisualStudioHostProject Project { get; } AbstractProject Project { get; }
/// <summary> /// <summary>
/// The Visual Studio identity of the document within its project. /// The Visual Studio identity of the document within its project.
...@@ -113,8 +113,8 @@ internal interface IVisualStudioHostDocument : IDisposable ...@@ -113,8 +113,8 @@ internal interface IVisualStudioHostDocument : IDisposable
void UpdateText(SourceText newText); void UpdateText(SourceText newText);
/// <summary> /// <summary>
/// Fetches the <see cref="ITextUndoHistory"/> that should be used to undo edits to this document. /// Fetches the <see cref="ITextBuffer"/> that should be used to undo edits to this document.
/// </summary> /// </summary>
ITextUndoHistory GetTextUndoHistory(); ITextBuffer GetTextUndoHistoryBuffer();
} }
} }
...@@ -2,48 +2,15 @@ ...@@ -2,48 +2,15 @@
using System; using System;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{ {
/// <summary> /// <summary>
/// The interface implemented by all types of projects within Visual Studio (like regular /// This interface only exists to maintain an overload of <see cref="DocumentProvider.TryGetDocumentForFile(IVisualStudioHostProject, string, CodeAnalysis.SourceCodeKind, System.Func{Text.ITextBuffer, bool}, System.Func{uint, System.Collections.Generic.IReadOnlyList{string}}, System.EventHandler, System.EventHandler{bool}, System.EventHandler{bool})"/>.
/// projects, Miscellaneous files projects, etc.)
/// </summary> /// </summary>
[Obsolete("This overload is a compatibility shim for TypeScript; please do not use it.")]
internal interface IVisualStudioHostProject internal interface IVisualStudioHostProject
{ {
ProjectId Id { get; } ProjectId Id { get; }
string Language { get; }
/// <summary>
/// The <see cref="IVsHierarchy"/> for this project. NOTE: May be null in Deferred Project Load cases.
/// </summary>
IVsHierarchy Hierarchy { get; }
Guid Guid { get; }
Microsoft.CodeAnalysis.Workspace Workspace { get; }
/// <summary>
/// The public display name of the project. This name is not unique and may be shared
/// between multiple projects, especially in cases like Venus where the intellisense
/// projects will match the name of their logical parent project.
/// </summary>
string DisplayName { get; }
/// <summary>
/// The name of the project according to the project system. In "regular" projects this is
/// equivalent to <see cref="DisplayName"/>, but in Venus cases these will differ. The
/// ProjectSystemName is the 2_Default.aspx project name, whereas the regular display name
/// matches the display name of the project the user actually sees in the solution explorer.
/// These can be assumed to be unique within the Visual Studio workspace.
/// </summary>
string ProjectSystemName { get; }
IVisualStudioHostDocument GetDocumentOrAdditionalDocument(DocumentId id);
IVisualStudioHostDocument GetCurrentDocumentFromPath(string filePath);
ProjectInfo CreateProjectInfoForCurrentState();
bool ContainsFile(string moniker);
} }
} }
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// An interface implemented by a workspace to get the set of host projects contained in the
/// workspace.
/// </summary>
internal interface IVisualStudioHostProjectContainer
{
IReadOnlyList<IVisualStudioHostProject> GetProjects();
void NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project);
}
}
...@@ -35,7 +35,7 @@ internal partial class InvisibleEditor : IInvisibleEditor ...@@ -35,7 +35,7 @@ internal partial class InvisibleEditor : IInvisibleEditor
/// <see cref="IVsUIShellOpenDocument4.IsDocumentInAProject2"/>, which performs a much slower query of all /// <see cref="IVsUIShellOpenDocument4.IsDocumentInAProject2"/>, which performs a much slower query of all
/// projects in the solution.</para> /// projects in the solution.</para>
/// </remarks> /// </remarks>
public InvisibleEditor(IServiceProvider serviceProvider, string filePath, IVisualStudioHostProject projectOpt, bool needsSave, bool needsUndoDisabled) public InvisibleEditor(IServiceProvider serviceProvider, string filePath, AbstractProject projectOpt, bool needsSave, bool needsUndoDisabled)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_filePath = filePath; _filePath = filePath;
......
...@@ -118,23 +118,23 @@ private IVsHierarchy GetSharedHierarchyForItemInternal(IVsHierarchy headProjectH ...@@ -118,23 +118,23 @@ private IVsHierarchy GetSharedHierarchyForItemInternal(IVsHierarchy headProjectH
: null; : null;
} }
public static IVisualStudioHostProject GetContextHostProject(IVsHierarchy sharedHierarchy, IVisualStudioHostProjectContainer hostProjectContainer) public static AbstractProject GetContextHostProject(IVsHierarchy sharedHierarchy, VisualStudioProjectTracker projectTracker)
{ {
return s_singleton.GetContextHostProjectInternal(sharedHierarchy, hostProjectContainer); return s_singleton.GetContextHostProjectInternal(sharedHierarchy, projectTracker);
} }
private IVisualStudioHostProject GetContextHostProjectInternal(IVsHierarchy hierarchy, IVisualStudioHostProjectContainer hostProjectContainer) private AbstractProject GetContextHostProjectInternal(IVsHierarchy hierarchy, VisualStudioProjectTracker projectTracker)
{ {
hierarchy = GetSharedItemContextHierarchy(hierarchy) ?? hierarchy; hierarchy = GetSharedItemContextHierarchy(hierarchy) ?? hierarchy;
var projectName = GetActiveIntellisenseProjectContextInternal(hierarchy); var projectName = GetActiveIntellisenseProjectContextInternal(hierarchy);
if (projectName != null) if (projectName != null)
{ {
return hostProjectContainer.GetProjects().FirstOrDefault(p => p.ProjectSystemName == projectName); return projectTracker.ImmutableProjects.FirstOrDefault(p => p.ProjectSystemName == projectName);
} }
else else
{ {
return hostProjectContainer.GetProjects().FirstOrDefault(p => p.Hierarchy == hierarchy); return projectTracker.ImmutableProjects.FirstOrDefault(p => p.Hierarchy == hierarchy);
} }
} }
......
...@@ -24,7 +24,7 @@ internal partial class VisualStudioMetadataReference ...@@ -24,7 +24,7 @@ internal partial class VisualStudioMetadataReference
/// When the compilation is recreated for a solution the compiler asks for metadata again and we need to provide the original content, /// When the compilation is recreated for a solution the compiler asks for metadata again and we need to provide the original content,
/// not read the file again. Therefore we need to save the timestamp on the <see cref="Snapshot"/>. /// not read the file again. Therefore we need to save the timestamp on the <see cref="Snapshot"/>.
/// ///
/// When the VS observes a change in a metadata reference file the <see cref="Project"/> version is advanced and a new instance of /// When the VS observes a change in a metadata reference file the project version is advanced and a new instance of
/// <see cref="Snapshot"/> is created for the corresponding reference. /// <see cref="Snapshot"/> is created for the corresponding reference.
/// </remarks> /// </remarks>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")] [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
......
...@@ -12,7 +12,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem ...@@ -12,7 +12,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
internal sealed partial class VisualStudioMetadataReference : IDisposable internal sealed partial class VisualStudioMetadataReference : IDisposable
{ {
private readonly VisualStudioMetadataReferenceManager _provider; private readonly VisualStudioMetadataReferenceManager _provider;
private readonly IVisualStudioHostProject _hostProject;
private readonly MetadataReferenceProperties _properties; private readonly MetadataReferenceProperties _properties;
private readonly FileChangeTracker _fileChangeTracker; private readonly FileChangeTracker _fileChangeTracker;
...@@ -22,14 +21,12 @@ internal sealed partial class VisualStudioMetadataReference : IDisposable ...@@ -22,14 +21,12 @@ internal sealed partial class VisualStudioMetadataReference : IDisposable
public VisualStudioMetadataReference( public VisualStudioMetadataReference(
VisualStudioMetadataReferenceManager provider, VisualStudioMetadataReferenceManager provider,
IVisualStudioHostProject hostProject,
string filePath, string filePath,
MetadataReferenceProperties properties) MetadataReferenceProperties properties)
{ {
Contract.ThrowIfTrue(properties.Kind != MetadataImageKind.Assembly); Contract.ThrowIfTrue(properties.Kind != MetadataImageKind.Assembly);
_provider = provider; _provider = provider;
_hostProject = hostProject;
_properties = properties; _properties = properties;
// We don't track changes to netmodules linked to the assembly. // We don't track changes to netmodules linked to the assembly.
...@@ -44,11 +41,6 @@ public string FilePath ...@@ -44,11 +41,6 @@ public string FilePath
get { return _fileChangeTracker.FilePath; } get { return _fileChangeTracker.FilePath; }
} }
public IVisualStudioHostProject Project
{
get { return _hostProject; }
}
public MetadataReferenceProperties Properties public MetadataReferenceProperties Properties
{ {
get { return _properties; } get { return _properties; }
......
...@@ -86,9 +86,9 @@ public PortableExecutableReference CreateMetadataReferenceSnapshot(string filePa ...@@ -86,9 +86,9 @@ public PortableExecutableReference CreateMetadataReferenceSnapshot(string filePa
return new VisualStudioMetadataReference.Snapshot(this, properties, filePath); return new VisualStudioMetadataReference.Snapshot(this, properties, filePath);
} }
public VisualStudioMetadataReference CreateMetadataReference(IVisualStudioHostProject hostProject, string filePath, MetadataReferenceProperties properties) public VisualStudioMetadataReference CreateMetadataReference(string filePath, MetadataReferenceProperties properties)
{ {
return new VisualStudioMetadataReference(this, hostProject, filePath, properties); return new VisualStudioMetadataReference(this, filePath, properties);
} }
public void ClearCache() public void ClearCache()
......
// 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.Diagnostics;
using EnvDTE;
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
using Workspace = Microsoft.CodeAnalysis.Workspace;
internal partial class MiscellaneousFilesWorkspace
{
private sealed class HostProject : IVisualStudioHostProject
{
public ProjectId Id { get; }
public string Language { get; }
internal IVisualStudioHostDocument Document { get; set; }
private readonly string _assemblyName;
private readonly ParseOptions _parseOptionsOpt;
private readonly CompilationOptions _compilationOptionsOpt;
private readonly IEnumerable<MetadataReference> _metadataReferences;
private readonly VersionStamp _version;
private readonly Workspace _workspace;
public HostProject(Workspace workspace, SolutionId solutionId, string languageName, ParseOptions parseOptionsOpt, CompilationOptions compilationOptionsOpt, IEnumerable<MetadataReference> metadataReferences)
{
Debug.Assert(workspace != null);
Debug.Assert(languageName != null);
Debug.Assert(metadataReferences != null);
_workspace = workspace;
_parseOptionsOpt = parseOptionsOpt;
_compilationOptionsOpt = compilationOptionsOpt;
Id = ProjectId.CreateNewId(debugName: "Miscellaneous Files");
Language = languageName;
// the assembly name must be unique for each collection of loose files. since the name doesn't matter
// a random GUID can be used.
_assemblyName = Guid.NewGuid().ToString("N");
_version = VersionStamp.Create();
_metadataReferences = metadataReferences;
}
public ProjectInfo CreateProjectInfoForCurrentState()
{
var info = ProjectInfo.Create(
Id,
_version,
name: ServicesVSResources.Miscellaneous_Files,
assemblyName: _assemblyName,
language: Language,
filePath: null,
compilationOptions: _compilationOptionsOpt,
parseOptions: _parseOptionsOpt,
documents: SpecializedCollections.EmptyEnumerable<DocumentInfo>(),
projectReferences: SpecializedCollections.EmptyEnumerable<ProjectReference>(),
metadataReferences: _metadataReferences,
isSubmission: false,
hostObjectType: null);
// misc project will never be fully loaded since, by defintion, it won't know
// what the full set of information is.
return info.WithHasAllInformation(hasAllInformation: false);
}
public Microsoft.VisualStudio.Shell.Interop.IVsHierarchy Hierarchy => null;
public Guid Guid => Guid.Empty;
public string ProjectType => Constants.vsProjectKindMisc;
public Workspace Workspace => _workspace;
public string DisplayName => "MiscellaneousFiles";
public string ProjectSystemName => DisplayName;
public IVisualStudioHostDocument GetDocumentOrAdditionalDocument(DocumentId id)
{
if (id == this.Document.Id)
{
return this.Document;
}
else
{
return null;
}
}
public IVisualStudioHostDocument GetCurrentDocumentFromPath(string filePath)
{
if (Document == null || !filePath.Equals(Document.Key.Moniker, StringComparison.OrdinalIgnoreCase))
{
return null;
}
return Document;
}
public bool ContainsFile(string moniker)
{
// We may have created our project but haven't created the document yet for it
if (Document == null)
{
return false;
}
return moniker.Equals(Document.Key.Moniker, StringComparison.OrdinalIgnoreCase);
}
}
}
}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.TextManager.Interop;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -25,15 +26,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem ...@@ -25,15 +26,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
using Workspace = Microsoft.CodeAnalysis.Workspace; using Workspace = Microsoft.CodeAnalysis.Workspace;
[Export(typeof(MiscellaneousFilesWorkspace))] [Export(typeof(MiscellaneousFilesWorkspace))]
internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunningDocTableEvents2, IVisualStudioHostProjectContainer internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunningDocTableEvents2
{ {
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService; private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService;
private readonly IMetadataAsSourceFileService _fileTrackingMetadataAsSourceService; private readonly IMetadataAsSourceFileService _fileTrackingMetadataAsSourceService;
private readonly IVsRunningDocumentTable4 _runningDocumentTable; private readonly IVsRunningDocumentTable4 _runningDocumentTable;
private readonly IVsTextManager _textManager; private readonly IVsTextManager _textManager;
private readonly DocumentProvider _documentProvider;
private readonly Dictionary<Guid, LanguageInformation> _languageInformationByLanguageGuid = new Dictionary<Guid, LanguageInformation>(); private readonly Dictionary<Guid, LanguageInformation> _languageInformationByLanguageGuid = new Dictionary<Guid, LanguageInformation>();
/// <summary> /// <summary>
...@@ -42,8 +41,11 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin ...@@ -42,8 +41,11 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin
/// </summary> /// </summary>
private IBidirectionalMap<uint, WorkspaceRegistration> _docCookieToWorkspaceRegistration = BidirectionalMap<uint, WorkspaceRegistration>.Empty; private IBidirectionalMap<uint, WorkspaceRegistration> _docCookieToWorkspaceRegistration = BidirectionalMap<uint, WorkspaceRegistration>.Empty;
private readonly Dictionary<ProjectId, HostProject> _hostProjects = new Dictionary<ProjectId, HostProject>(); /// <summary>
private readonly Dictionary<uint, HostProject> _docCookiesToHostProject = new Dictionary<uint, HostProject>(); /// The mapping of all doc cookies in the RDT and the <see cref="ProjectId"/> of the project and <see cref="SourceTextContainer"/> of the open
/// file we have created for that open buffer. An entry should only be in here if it's also already in <see cref="_docCookieToWorkspaceRegistration"/>.
/// </summary>
private readonly Dictionary<uint, (ProjectId projectId, SourceTextContainer textContainer)> _docCookiesToProjectIdAndContainer = new Dictionary<uint, (ProjectId, SourceTextContainer)>();
private readonly ImmutableArray<MetadataReference> _metadataReferences; private readonly ImmutableArray<MetadataReference> _metadataReferences;
private uint _runningDocumentTableEventsCookie; private uint _runningDocumentTableEventsCookie;
...@@ -69,7 +71,6 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin ...@@ -69,7 +71,6 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunnin
((IVsRunningDocumentTable)_runningDocumentTable).AdviseRunningDocTableEvents(this, out _runningDocumentTableEventsCookie); ((IVsRunningDocumentTable)_runningDocumentTable).AdviseRunningDocTableEvents(this, out _runningDocumentTableEventsCookie);
_metadataReferences = ImmutableArray.CreateRange(CreateMetadataReferences()); _metadataReferences = ImmutableArray.CreateRange(CreateMetadataReferences());
_documentProvider = new DocumentProvider(this, serviceProvider, documentTrackingService: null);
saveEventsService.StartSendingSaveEvents(); saveEventsService.StartSendingSaveEvents();
} }
...@@ -140,7 +141,7 @@ public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarch ...@@ -140,7 +141,7 @@ public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarch
{ {
var moniker = _runningDocumentTable.GetDocumentMoniker(docCookie); var moniker = _runningDocumentTable.GetDocumentMoniker(docCookie);
if (moniker != null && TryGetLanguageInformation(moniker) != null && !_docCookiesToHostProject.ContainsKey(docCookie)) if (moniker != null && TryGetLanguageInformation(moniker) != null && !_docCookiesToProjectIdAndContainer.ContainsKey(docCookie))
{ {
TrackOpenedDocument(docCookie, moniker); TrackOpenedDocument(docCookie, moniker);
} }
...@@ -256,15 +257,12 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e) ...@@ -256,15 +257,12 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e)
if (workspaceRegistration.Workspace == null) if (workspaceRegistration.Workspace == null)
{ {
if (_docCookiesToHostProject.TryGetValue(docCookie, out var hostProject)) if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndSourceTextContainer))
{ {
// The workspace was taken from us and released and we have only asynchronously found out now. // The workspace was taken from us and released and we have only asynchronously found out now.
var document = hostProject.Document; // We already have the file open in our workspace, but the global mapping of source text container
// to the workspace that owns it needs to be updated once more.
if (document.IsOpen) RegisterText(projectIdAndSourceTextContainer.textContainer);
{
RegisterText(document.GetOpenTextContainer());
}
} }
else else
{ {
...@@ -281,7 +279,7 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e) ...@@ -281,7 +279,7 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e)
else if (IsClaimedByAnotherWorkspace(workspaceRegistration)) else if (IsClaimedByAnotherWorkspace(workspaceRegistration))
{ {
// It's now claimed by another workspace, so we should unclaim it // It's now claimed by another workspace, so we should unclaim it
if (_docCookiesToHostProject.ContainsKey(docCookie)) if (_docCookiesToProjectIdAndContainer.ContainsKey(docCookie))
{ {
DetachFromDocument(docCookie, moniker); DetachFromDocument(docCookie, moniker);
} }
...@@ -330,17 +328,32 @@ private void AttachToDocument(uint docCookie, string moniker) ...@@ -330,17 +328,32 @@ private void AttachToDocument(uint docCookie, string moniker)
return; return;
} }
var projectInfo = CreateProjectInfoForDocument(moniker);
OnProjectAdded(projectInfo);
var sourceTextContainer = textBuffer.AsTextContainer();
OnDocumentOpened(projectInfo.Documents.Single().Id, sourceTextContainer);
_docCookiesToProjectIdAndContainer.Add(docCookie, (projectInfo.Id, sourceTextContainer));
}
/// <summary>
/// Creates the <see cref="ProjectInfo"/> that can be added to the workspace for a newly opened document.
/// </summary>
private ProjectInfo CreateProjectInfoForDocument(string filePath)
{
// This should always succeed since we only got here if we already confirmed the moniker is acceptable // This should always succeed since we only got here if we already confirmed the moniker is acceptable
var languageInformation = TryGetLanguageInformation(moniker); var languageInformation = TryGetLanguageInformation(filePath);
Contract.ThrowIfNull(languageInformation); Contract.ThrowIfNull(languageInformation);
var languageServices = Services.GetLanguageServices(languageInformation.LanguageName); var languageServices = Services.GetLanguageServices(languageInformation.LanguageName);
var compilationOptionsOpt = languageServices.GetService<ICompilationFactoryService>()?.GetDefaultCompilationOptions(); var compilationOptionsOpt = languageServices.GetService<ICompilationFactoryService>()?.GetDefaultCompilationOptions();
var parseOptionsOpt = languageServices.GetService<ISyntaxTreeFactoryService>()?.GetDefaultParseOptions(); var parseOptionsOpt = languageServices.GetService<ISyntaxTreeFactoryService>()?.GetDefaultParseOptions();
if (parseOptionsOpt != null && if (parseOptionsOpt != null &&
compilationOptionsOpt != null && compilationOptionsOpt != null &&
PathUtilities.GetExtension(moniker) == languageInformation.ScriptExtension) PathUtilities.GetExtension(filePath) == languageInformation.ScriptExtension)
{ {
parseOptionsOpt = parseOptionsOpt.WithKind(SourceCodeKind.Script); parseOptionsOpt = parseOptionsOpt.WithKind(SourceCodeKind.Script);
...@@ -350,7 +363,7 @@ private void AttachToDocument(uint docCookie, string moniker) ...@@ -350,7 +363,7 @@ private void AttachToDocument(uint docCookie, string moniker)
// Misc files workspace always provides the service: // Misc files workspace always provides the service:
Contract.ThrowIfNull(scriptEnvironmentService); Contract.ThrowIfNull(scriptEnvironmentService);
var baseDirectory = PathUtilities.GetDirectoryName(moniker); var baseDirectory = PathUtilities.GetDirectoryName(filePath);
// TODO (https://github.com/dotnet/roslyn/issues/5325, https://github.com/dotnet/roslyn/issues/13886): // TODO (https://github.com/dotnet/roslyn/issues/5325, https://github.com/dotnet/roslyn/issues/13886):
// - Need to have a way to specify these somewhere in VS options. // - Need to have a way to specify these somewhere in VS options.
...@@ -367,40 +380,34 @@ private void AttachToDocument(uint docCookie, string moniker) ...@@ -367,40 +380,34 @@ private void AttachToDocument(uint docCookie, string moniker)
WithSourceReferenceResolver(new SourceFileResolver(scriptEnvironmentService.SourceReferenceSearchPaths, baseDirectory)); WithSourceReferenceResolver(new SourceFileResolver(scriptEnvironmentService.SourceReferenceSearchPaths, baseDirectory));
} }
// First, create the project var projectId = ProjectId.CreateNewId(debugName: "Miscellaneous Files Project for " + filePath);
var hostProject = new HostProject(this, CurrentSolution.Id, languageInformation.LanguageName, parseOptionsOpt, compilationOptionsOpt, _metadataReferences); var documentId = DocumentId.CreateNewId(projectId, debugName: filePath);
// Now try to find the document. We accept any text buffer, since we've already verified it's an appropriate file in ShouldIncludeFile. var documentInfo = DocumentInfo.Create(
var document = _documentProvider.TryGetDocumentForFile( documentId,
hostProject, filePath,
moniker, sourceCodeKind: parseOptionsOpt?.Kind ?? SourceCodeKind.Regular,
parseOptionsOpt?.Kind ?? SourceCodeKind.Regular, loader: new FileTextLoader(filePath, defaultEncoding: null),
getFolderNames: _ => SpecializedCollections.EmptyReadOnlyList<string>(), filePath: filePath);
canUseTextBuffer: _ => true);
// The assembly name must be unique for each collection of loose files. Since the name doesn't matter
// If the buffer has not yet been initialized, we won't get a document. // a random GUID can be used.
if (document == null) string assemblyName = Guid.NewGuid().ToString("N");
{
return; var projectInfo = ProjectInfo.Create(
} projectId,
VersionStamp.Create(),
// Since we have a document, we can do the rest of the project setup. name: ServicesVSResources.Miscellaneous_Files,
_hostProjects.Add(hostProject.Id, hostProject); assemblyName,
OnProjectAdded(hostProject.CreateProjectInfoForCurrentState()); languageInformation.LanguageName,
compilationOptions: compilationOptionsOpt,
OnDocumentAdded(document.GetInitialState()); parseOptions: parseOptionsOpt,
hostProject.Document = document; documents: SpecializedCollections.SingletonEnumerable(documentInfo),
metadataReferences: _metadataReferences);
// Notify the document provider, so it knows the document is now open and a part of
// the project // Miscellaneous files projects are never fully loaded since, by definition, it won't know
_documentProvider.NotifyDocumentRegisteredToProjectAndStartToRaiseEvents(document); // what the full set of information is.
return projectInfo.WithHasAllInformation(hasAllInformation: false);
Contract.ThrowIfFalse(document.IsOpen);
var buffer = document.GetOpenTextBuffer();
OnDocumentOpened(document.Id, document.GetOpenTextContainer());
_docCookiesToHostProject.Add(docCookie, hostProject);
} }
private void DetachFromDocument(uint docCookie, string moniker) private void DetachFromDocument(uint docCookie, string moniker)
...@@ -411,18 +418,15 @@ private void DetachFromDocument(uint docCookie, string moniker) ...@@ -411,18 +418,15 @@ private void DetachFromDocument(uint docCookie, string moniker)
return; return;
} }
if (_docCookiesToHostProject.TryGetValue(docCookie, out var hostProject)) if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndContainer))
{ {
var document = hostProject.Document; var document = this.CurrentSolution.GetProject(projectIdAndContainer.projectId).Documents.Single();
OnDocumentClosed(document.Id, document.Loader);
OnDocumentRemoved(document.Id);
OnProjectRemoved(hostProject.Id);
_hostProjects.Remove(hostProject.Id); // We must close the document prior to deleting the project
_docCookiesToHostProject.Remove(docCookie); OnDocumentClosed(document.Id, new FileTextLoader(document.FilePath, defaultEncoding: null));
OnProjectRemoved(document.Project.Id);
document.Dispose(); _docCookiesToProjectIdAndContainer.Remove(docCookie);
return; return;
} }
...@@ -452,35 +456,14 @@ public override bool CanApplyChange(ApplyChangesKind feature) ...@@ -452,35 +456,14 @@ public override bool CanApplyChange(ApplyChangesKind feature)
protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText)
{ {
var hostDocument = this.GetDocument(documentId); foreach (var projectIdAndSourceTextContainer in _docCookiesToProjectIdAndContainer.Values)
hostDocument.UpdateText(newText);
}
private HostProject GetHostProject(ProjectId id)
{
_hostProjects.TryGetValue(id, out var project);
return project;
}
internal IVisualStudioHostDocument GetDocument(DocumentId id)
{
var project = GetHostProject(id.ProjectId);
if (project != null && project.Document.Id == id)
{ {
return project.Document; if (projectIdAndSourceTextContainer.projectId == documentId.ProjectId)
{
TextEditApplication.UpdateText(newText, projectIdAndSourceTextContainer.textContainer.GetTextBuffer(), EditOptions.DefaultMinimalChange);
break;
}
} }
return null;
}
IReadOnlyList<IVisualStudioHostProject> IVisualStudioHostProjectContainer.GetProjects()
{
return _hostProjects.Values.ToImmutableReadOnlyListOrEmpty<IVisualStudioHostProject>();
}
void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project)
{
// Since the MiscellaneousFilesWorkspace doesn't do anything lazily, this is a no-op
} }
private class LanguageInformation private class LanguageInformation
......
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Undo;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
internal static class TextEditApplication
{
internal static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options)
{
using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null))
{
var oldSnapshot = buffer.CurrentSnapshot;
var oldText = oldSnapshot.AsText();
var changes = newText.GetTextChanges(oldText);
if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace))
{
var undoService = workspace.Services.GetService<ISourceTextUndoService>();
undoService.BeginUndoTransaction(oldSnapshot);
}
foreach (var change in changes)
{
edit.Replace(change.Span.Start, change.Span.Length, change.NewText);
}
edit.ApplyAndLogExceptions();
}
}
}
}
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{ {
internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffinitizedObject, IVisualStudioHostProjectContainer internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffinitizedObject
{ {
#region Readonly fields #region Readonly fields
private static readonly ConditionalWeakTable<SolutionId, string> s_workingFolderPathMap = new ConditionalWeakTable<SolutionId, string>(); private static readonly ConditionalWeakTable<SolutionId, string> s_workingFolderPathMap = new ConditionalWeakTable<SolutionId, string>();
...@@ -92,9 +92,7 @@ internal ImmutableArray<AbstractProject> ImmutableProjects ...@@ -92,9 +92,7 @@ internal ImmutableArray<AbstractProject> ImmutableProjects
internal HostWorkspaceServices WorkspaceServices { get; } internal HostWorkspaceServices WorkspaceServices { get; }
IReadOnlyList<IVisualStudioHostProject> IVisualStudioHostProjectContainer.GetProjects() => this.ImmutableProjects; internal void NotifyNonDocumentOpenedForProject(AbstractProject project)
void IVisualStudioHostProjectContainer.NotifyNonDocumentOpenedForProject(IVisualStudioHostProject project)
{ {
AssertIsForeground(); AssertIsForeground();
......
...@@ -108,12 +108,12 @@ internal IVisualStudioHostDocument GetHostDocument(DocumentId documentId) ...@@ -108,12 +108,12 @@ internal IVisualStudioHostDocument GetHostDocument(DocumentId documentId)
return null; return null;
} }
internal IVisualStudioHostProject GetHostProject(ProjectId projectId) internal AbstractProject GetHostProject(ProjectId projectId)
{ {
return DeferredState?.ProjectTracker.GetProject(projectId); return DeferredState?.ProjectTracker.GetProject(projectId);
} }
private bool TryGetHostProject(ProjectId projectId, out IVisualStudioHostProject project) private bool TryGetHostProject(ProjectId projectId, out AbstractProject project)
{ {
project = GetHostProject(projectId); project = GetHostProject(projectId);
return project != null; return project != null;
...@@ -241,7 +241,7 @@ public override bool CanApplyChange(ApplyChangesKind feature) ...@@ -241,7 +241,7 @@ public override bool CanApplyChange(ApplyChangesKind feature)
} }
} }
private bool TryGetProjectData(ProjectId projectId, out IVisualStudioHostProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) private bool TryGetProjectData(ProjectId projectId, out AbstractProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project)
{ {
hierarchy = null; hierarchy = null;
project = null; project = null;
...@@ -251,7 +251,7 @@ private bool TryGetProjectData(ProjectId projectId, out IVisualStudioHostProject ...@@ -251,7 +251,7 @@ private bool TryGetProjectData(ProjectId projectId, out IVisualStudioHostProject
&& hierarchy.TryGetProject(out project); && hierarchy.TryGetProject(out project);
} }
internal void GetProjectData(ProjectId projectId, out IVisualStudioHostProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project) internal void GetProjectData(ProjectId projectId, out AbstractProject hostProject, out IVsHierarchy hierarchy, out EnvDTE.Project project)
{ {
if (!TryGetProjectData(projectId, out hostProject, out hierarchy, out project)) if (!TryGetProjectData(projectId, out hostProject, out hierarchy, out project))
{ {
...@@ -622,7 +622,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa ...@@ -622,7 +622,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa
#endif #endif
private ProjectItem AddDocumentToProject( private ProjectItem AddDocumentToProject(
IVisualStudioHostProject hostProject, AbstractProject hostProject,
EnvDTE.Project project, EnvDTE.Project project,
DocumentId documentId, DocumentId documentId,
string documentName, string documentName,
...@@ -641,7 +641,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa ...@@ -641,7 +641,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa
} }
private ProjectItem AddDocumentToFolder( private ProjectItem AddDocumentToFolder(
IVisualStudioHostProject hostProject, AbstractProject hostProject,
EnvDTE.Project project, EnvDTE.Project project,
DocumentId documentId, DocumentId documentId,
IEnumerable<string> folders, IEnumerable<string> folders,
...@@ -662,7 +662,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa ...@@ -662,7 +662,7 @@ protected override void AddExistingDocument(DocumentId documentId, string filePa
} }
private ProjectItem AddDocumentToProjectItems( private ProjectItem AddDocumentToProjectItems(
IVisualStudioHostProject hostProject, AbstractProject hostProject,
ProjectItems projectItems, ProjectItems projectItems,
DocumentId documentId, DocumentId documentId,
string folderPath, string folderPath,
...@@ -865,7 +865,7 @@ protected override void ApplyAdditionalDocumentTextChanged(DocumentId documentId ...@@ -865,7 +865,7 @@ protected override void ApplyAdditionalDocumentTextChanged(DocumentId documentId
hostDocument.UpdateText(newText); hostDocument.UpdateText(newText);
} }
private static string GetPreferredExtension(IVisualStudioHostProject hostProject, SourceCodeKind sourceCodeKind) private static string GetPreferredExtension(AbstractProject hostProject, SourceCodeKind sourceCodeKind)
{ {
// No extension was provided. Pick a good one based on the type of host project. // No extension was provided. Pick a good one based on the type of host project.
switch (hostProject.Language) switch (hostProject.Language)
......
...@@ -181,8 +181,6 @@ public bool IsOpen ...@@ -181,8 +181,6 @@ public bool IsOpen
#pragma warning restore 67 #pragma warning restore 67
IVisualStudioHostProject IVisualStudioHostDocument.Project { get { return this.Project; } }
public ITextBuffer GetOpenTextBuffer() public ITextBuffer GetOpenTextBuffer()
{ {
return _containedLanguage.SubjectBuffer; return _containedLanguage.SubjectBuffer;
...@@ -1150,10 +1148,10 @@ private bool CheckCode(ITextSnapshot snapshot, int position, char ch, string tag ...@@ -1150,10 +1148,10 @@ private bool CheckCode(ITextSnapshot snapshot, int position, char ch, string tag
return CheckCode(snapshot, position - tag2.Length, tag1); return CheckCode(snapshot, position - tag2.Length, tag1);
} }
public ITextUndoHistory GetTextUndoHistory() public ITextBuffer GetTextUndoHistoryBuffer()
{ {
// In Venus scenarios, the undo history is associated with the data buffer // In Venus scenarios, the undo history is associated with the data buffer
return _componentModel.GetService<ITextUndoHistoryRegistry>().GetHistory(_containedLanguage.DataBuffer); return _containedLanguage.DataBuffer;
} }
public uint GetItemId() public uint GetItemId()
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // 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.Composition; using System.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host;
...@@ -25,9 +20,9 @@ internal class VisualStudioTextUndoHistoryWorkspaceServiceFactory : IWorkspaceSe ...@@ -25,9 +20,9 @@ internal class VisualStudioTextUndoHistoryWorkspaceServiceFactory : IWorkspaceSe
private readonly ITextUndoHistoryWorkspaceService _serviceSingleton; private readonly ITextUndoHistoryWorkspaceService _serviceSingleton;
[ImportingConstructor] [ImportingConstructor]
public VisualStudioTextUndoHistoryWorkspaceServiceFactory() public VisualStudioTextUndoHistoryWorkspaceServiceFactory(ITextUndoHistoryRegistry undoHistoryRegistry)
{ {
_serviceSingleton = new TextUndoHistoryWorkspaceService(); _serviceSingleton = new TextUndoHistoryWorkspaceService(undoHistoryRegistry);
} }
public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
...@@ -37,44 +32,48 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) ...@@ -37,44 +32,48 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
private class TextUndoHistoryWorkspaceService : ITextUndoHistoryWorkspaceService private class TextUndoHistoryWorkspaceService : ITextUndoHistoryWorkspaceService
{ {
public bool TryGetTextUndoHistory(Workspace editorWorkspace, ITextBuffer textBuffer, out ITextUndoHistory undoHistory) private ITextUndoHistoryRegistry _undoHistoryRegistry;
public TextUndoHistoryWorkspaceService(ITextUndoHistoryRegistry undoHistoryRegistry)
{ {
undoHistory = null; _undoHistoryRegistry = undoHistoryRegistry;
}
if (!(editorWorkspace is VisualStudioWorkspaceImpl) && public bool TryGetTextUndoHistory(Workspace editorWorkspace, ITextBuffer textBuffer, out ITextUndoHistory undoHistory)
!(editorWorkspace is MiscellaneousFilesWorkspace)) {
switch (editorWorkspace)
{ {
return false; case VisualStudioWorkspaceImpl visualStudioWorkspace:
}
// TODO: Handle undo if context changes // TODO: Handle undo if context changes
var documentId = editorWorkspace.GetDocumentIdInCurrentContext(textBuffer.AsTextContainer()); var documentId = editorWorkspace.GetDocumentIdInCurrentContext(textBuffer.AsTextContainer());
if (documentId == null) if (documentId == null)
{ {
return false; undoHistory = null;
} return false;
}
var document = GetDocument(editorWorkspace, documentId); // In the Visual Studio case, there might be projection buffers involved for Venus,
if (document == null) // where we associate undo history with the surface buffer and not the subject buffer.
{ textBuffer = visualStudioWorkspace.GetHostDocument(documentId).GetTextUndoHistoryBuffer();
return false;
}
undoHistory = document.GetTextUndoHistory(); break;
return true;
} case MiscellaneousFilesWorkspace miscellaneousFilesWorkspace:
// Nothing to do in this case: textBuffer is correct!
break;
default:
undoHistory = null;
return false;
private IVisualStudioHostDocument GetDocument(Workspace workspace, DocumentId id)
{
switch (workspace)
{
case VisualStudioWorkspaceImpl visualStudioWorkspace:
return visualStudioWorkspace.GetHostDocument(id);
case MiscellaneousFilesWorkspace miscWorkspace:
return miscWorkspace.GetDocument(id);
} }
return null; undoHistory = _undoHistoryRegistry.GetHistory(textBuffer);
return true;
} }
} }
} }
......
...@@ -19,7 +19,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim ...@@ -19,7 +19,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim
Partial Friend MustInherit Class VisualBasicProject Partial Friend MustInherit Class VisualBasicProject
Inherits AbstractLegacyProject Inherits AbstractLegacyProject
Implements IVbCompilerProject Implements IVbCompilerProject
Implements IVisualStudioHostProject
Private ReadOnly _compilerHost As IVbCompilerHost Private ReadOnly _compilerHost As IVbCompilerHost
Private ReadOnly _imports As New List(Of GlobalImport) Private ReadOnly _imports As New List(Of GlobalImport)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册