提交 583cbf4e 编写于 作者: C CyrusNajmabadi

Move DefinitionItem closer to a pure data model.

Right now we have specialized subclasses of this type for different purposes.
However, those subclasses are problematic in terms of OOP as we would need
to have specialized knowledge of all these different types to properly remote them.
上级 d01211ef
......@@ -396,8 +396,6 @@
<Compile Include="DocumentSpan.cs" />
<Compile Include="FindUsages\DefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.DocumentLocationDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.NonNavigatingDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.SymbolDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionsAndReferences.cs" />
<Compile Include="FindUsages\SourceReferenceItem.cs" />
<Compile Include="Diagnostics\EngineV2\DiagnosticAnalyzerExecutor.cs" />
......
// 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.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindUsages
{
......@@ -11,25 +17,112 @@ internal partial class DefinitionItem
/// <see cref="DocumentSpan"/>.
/// </summary>
// internal for testing purposes.
internal sealed class DocumentLocationDefinitionItem : DefinitionItem
internal sealed class DefaultDefinitionItem : DefinitionItem
{
private readonly Workspace _workspaceOpt;
internal override bool IsExternal => false;
public DocumentLocationDefinitionItem(
public DefaultDefinitionItem(
Workspace workspaceOpt,
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> nameDisplayParts,
ImmutableArray<TaggedText> originationParts,
ImmutableArray<DocumentSpan> sourceSpans,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences)
: base(tags, displayParts, nameDisplayParts,
ImmutableArray.Create(new TaggedText(TextTags.Text, sourceSpans[0].Document.Project.Name)),
: base(tags, displayParts, nameDisplayParts, originationParts,
sourceSpans, properties, displayIfNoReferences)
{
_workspaceOpt = workspaceOpt;
}
public override bool CanNavigateTo() => SourceSpans[0].CanNavigateTo();
public override bool TryNavigateTo() => SourceSpans[0].TryNavigateTo();
public override bool CanNavigateTo()
{
if (this.Properties.ContainsKey(NonNavigable))
{
return false;
}
if (this.Properties.TryGetValue(MetadataSymbolKey, out var symbolKey))
{
return CanNavigateToMetadataSymbol(symbolKey);
}
return SourceSpans[0].CanNavigateTo();
}
public override bool TryNavigateTo()
{
if (this.Properties.ContainsKey(NonNavigable))
{
return false;
}
if (this.Properties.TryGetValue(MetadataSymbolKey, out var symbolKey))
{
return TryNavigateToMetadataSymbol(symbolKey);
}
return SourceSpans[0].TryNavigateTo();
}
private bool CanNavigateToMetadataSymbol(string symbolKey)
=> TryNavigateToMetadataSymbol(symbolKey, (symbol, project, service) => true);
private bool TryNavigateToMetadataSymbol(string symbolKey)
=> TryNavigateToMetadataSymbol(symbolKey, (symbol, project, service) =>
service.TryNavigateToSymbol(
symbol, project, project.Solution.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true)));
private bool TryNavigateToMetadataSymbol(string symbolKey, Func<ISymbol, Project, ISymbolNavigationService, bool> action)
{
var projectAndSymbol = ResolveSymbolInCurrentSolution(symbolKey);
if (projectAndSymbol == null)
{
return false;
}
var project = projectAndSymbol?.project;
var symbol = projectAndSymbol?.symbol;
if (symbol == null || project == null)
{
return false;
}
if (symbol.Kind == SymbolKind.Namespace)
{
return false;
}
var navigationService = _workspaceOpt.Services.GetService<ISymbolNavigationService>();
return action(symbol, project, navigationService);
}
private (Project project, ISymbol symbol)? ResolveSymbolInCurrentSolution(string symbolKey)
{
if (!this.Properties.TryGetValue(MetadataAssemblyIdentityDisplayName, out var identityDisplayName) ||
!AssemblyIdentity.TryParseDisplayName(identityDisplayName, out var identity))
{
return null;
}
var project = _workspaceOpt.CurrentSolution
.ProjectsWithReferenceToAssembly(identity)
.FirstOrDefault();
if (project == null)
{
return null;
}
var compilation = project.GetCompilationAsync(CancellationToken.None)
.WaitAndGetResult(CancellationToken.None);
var symbol = SymbolKey.Resolve(symbolKey, compilation).Symbol;
return (project, symbol);
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
/// <summary>
/// Implementation of a <see cref="DefinitionItem"/> used for definitions
/// that cannot be navigated to. For example, C# and VB namespaces cannot be
/// navigated to.
/// </summary>
private sealed class NonNavigatingDefinitionItem : DefinitionItem
{
internal override bool IsExternal => false;
public NonNavigatingDefinitionItem(
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> originationParts,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences)
: base(tags, displayParts, ImmutableArray<TaggedText>.Empty,
originationParts, ImmutableArray<DocumentSpan>.Empty,
properties, displayIfNoReferences)
{
}
public override bool CanNavigateTo() => false;
public override bool TryNavigateTo() => false;
}
}
}
\ No newline at end of file
// 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.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
/// <summary>
/// Implementation of a <see cref="DefinitionItem"/> that sits on top of an
/// <see cref="ISymbol"/>. In order to not keep anything alive too long, we only
/// hold onto IDs and Keys. When the user tries to navigate to an item we will
/// attempt to find the symbol again in the current solution snapshot and
/// navigate to it there.
/// </summary>
private sealed class MetadataDefinitionItem : DefinitionItem
{
private readonly Workspace _workspace;
private readonly SymbolKey _symbolKey;
private readonly AssemblyIdentity _symbolAssemblyIdentity;
internal override bool IsExternal => false;
public MetadataDefinitionItem(
ImmutableArray<string> tags,
ImmutableArray<TaggedText> displayParts,
ImmutableArray<TaggedText> nameDisplayParts,
ImmutableDictionary<string, string> properties,
bool displayIfNoReferences,
Solution solution, ISymbol definition)
: base(tags, displayParts, nameDisplayParts,
GetOriginationParts(definition),
ImmutableArray<DocumentSpan>.Empty,
properties,
displayIfNoReferences)
{
_workspace = solution.Workspace;
_symbolKey = definition.GetSymbolKey();
_symbolAssemblyIdentity = definition.ContainingAssembly?.Identity;
}
public override bool CanNavigateTo()
=> TryNavigateTo((symbol, project, service) => true);
public override bool TryNavigateTo()
{
return TryNavigateTo((symbol, project, service) =>
service.TryNavigateToSymbol(
symbol, project, project.Solution.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true)));
}
private bool TryNavigateTo(Func<ISymbol, Project, ISymbolNavigationService, bool> action)
{
var projectAndSymbol = ResolveSymbolInCurrentSolution();
if (projectAndSymbol == null)
{
return false;
}
var project = projectAndSymbol?.project;
var symbol = projectAndSymbol?.symbol;
if (symbol == null || project == null)
{
return false;
}
if (symbol.Kind == SymbolKind.Namespace)
{
return false;
}
var navigationService = _workspace.Services.GetService<ISymbolNavigationService>();
return action(symbol, project, navigationService);
}
private (Project project, ISymbol symbol)? ResolveSymbolInCurrentSolution()
{
var project = _workspace.CurrentSolution
.ProjectsWithReferenceToAssembly(_symbolAssemblyIdentity)
.FirstOrDefault();
if (project == null)
{
return null;
}
var compilation = project.GetCompilationAsync(CancellationToken.None)
.WaitAndGetResult(CancellationToken.None);
return (project, _symbolKey.Resolve(compilation).Symbol);
}
}
}
}
\ No newline at end of file
......@@ -18,6 +18,10 @@ namespace Microsoft.CodeAnalysis.FindUsages
/// </summary>
internal abstract partial class DefinitionItem
{
private const string MetadataSymbolKey = nameof(MetadataSymbolKey);
private const string MetadataAssemblyIdentityDisplayName = nameof(MetadataAssemblyIdentityDisplayName);
private const string NonNavigable = nameof(NonNavigable);
/// <summary>
/// Descriptive tags from <see cref="CompletionTags"/>. These tags may influence how the
/// item is displayed.
......@@ -127,8 +131,14 @@ internal abstract partial class DefinitionItem
throw new ArgumentException($"{nameof(sourceSpans)} cannot be empty.");
}
return new DocumentLocationDefinitionItem(
tags, displayParts, nameDisplayParts, sourceSpans, properties, displayIfNoReferences);
var firstDocument = sourceSpans[0].Document;
var originationParts = ImmutableArray.Create(
new TaggedText(TextTags.Text, firstDocument.Project.Name));
return new DefaultDefinitionItem(
firstDocument.Project.Solution.Workspace,
tags, displayParts, nameDisplayParts, originationParts,
sourceSpans, properties, displayIfNoReferences);
}
internal static DefinitionItem CreateMetadataDefinition(
......@@ -139,9 +149,21 @@ internal abstract partial class DefinitionItem
ImmutableDictionary<string, string> properties = null,
bool displayIfNoReferences = true)
{
return new MetadataDefinitionItem(
tags, displayParts, nameDisplayParts, properties,
displayIfNoReferences, solution, symbol);
properties = properties ?? ImmutableDictionary<string, string>.Empty;
var symbolKey = symbol.GetSymbolKey().ToString();
var assemblyIdentityDisplayName = symbol.ContainingAssembly?.Identity.GetDisplayName();
properties = properties.Add(MetadataSymbolKey, symbolKey)
.Add(MetadataAssemblyIdentityDisplayName, assemblyIdentityDisplayName);
var originationParts = GetOriginationParts(symbol);
return new DefaultDefinitionItem(
solution.Workspace, tags,
displayParts, nameDisplayParts, originationParts,
sourceSpans: ImmutableArray<DocumentSpan>.Empty,
properties: properties,
displayIfNoReferences: displayIfNoReferences);
}
// Kept around for binary compat with F#/TypeScript.
......@@ -163,8 +185,18 @@ internal abstract partial class DefinitionItem
ImmutableDictionary<string, string> properties = null,
bool displayIfNoReferences = true)
{
return new NonNavigatingDefinitionItem(
tags, displayParts, originationParts, properties, displayIfNoReferences);
properties = properties ?? ImmutableDictionary<string, string>.Empty;
properties = properties.Add(NonNavigable, NonNavigable);
return new DefaultDefinitionItem(
workspaceOpt: null,
tags: tags,
displayParts: displayParts,
nameDisplayParts: ImmutableArray<TaggedText>.Empty,
originationParts: originationParts,
sourceSpans: ImmutableArray<DocumentSpan>.Empty,
properties: properties,
displayIfNoReferences: displayIfNoReferences);
}
internal static ImmutableArray<TaggedText> GetOriginationParts(ISymbol symbol)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindReferences
{
internal partial class DefinitionLocation
{
/// <summary>
/// Implementation of a <see cref="DefinitionLocation"/> that sits on top of a
/// <see cref="DocumentLocation"/>.
/// </summary>
// Internal for testing purposes only.
internal sealed class DocumentDefinitionLocation : DefinitionLocation
{
public readonly DocumentLocation Location;
public DocumentDefinitionLocation(DocumentLocation location)
{
Location = location;
}
/// <summary>
/// Show the project that this <see cref="DocumentLocation"/> is contained in as the
/// Origination of this <see cref="DefinitionLocation"/>.
/// </summary>
public override ImmutableArray<TaggedText> OriginationParts =>
ImmutableArray.Create(new TaggedText(TextTags.Text, Location.Document.Project.Name));
public override bool CanNavigateTo() => true;
public override bool TryNavigateTo() => Location.TryNavigateTo();
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册