提交 90d042a9 编写于 作者: D Dustin Campbell

Rationalize Code Model "member node" logic

Code Model has several ways of looking at the member nodes of a
particular container. Sometimes it does so recursively, flattening all
nested members into a single list. Sometimes, it looks for "logical"
nodes, i.e. breaking up field declarations into their component
declarators. Sometimes only supported nodes (that is, nodes that can be
properly represented in Code Mode) are expected.

Unfortunately, the internal APIs for these searches grew up organically
and have ecome quite a mess. There were several places where the wrong
search was being used, resulting in strange bugs*.

These concepts are now consolidated into a single GetMemberNodes()
method and all callsites have been reviewed and updated.

* The particular bug that this change addresses is about inserting a
node into a container that contains an incomplete member. In that case,
the code used a "flattened" list to find the index where the node was to
be inserted. What it really wanted was a "logical" list, but that was
conflated with "flattening".
上级 e68db6df
...@@ -361,113 +361,112 @@ public override IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent) ...@@ -361,113 +361,112 @@ public override IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent)
return SpecializedCollections.EmptyEnumerable<SyntaxNode>(); return SpecializedCollections.EmptyEnumerable<SyntaxNode>();
} }
private static bool HasMembers(SyntaxNode node) private static bool IsContainerNode(SyntaxNode container)
{ {
switch (node.Kind()) return container is CompilationUnitSyntax
{ || container is NamespaceDeclarationSyntax
case SyntaxKind.CompilationUnit: || container is TypeDeclarationSyntax
case SyntaxKind.NamespaceDeclaration: || container is EnumDeclarationSyntax;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.EnumDeclaration:
return true;
default:
return false;
}
} }
private static IEnumerable<SyntaxNode> GetFlattenedMembers(IEnumerable<MemberDeclarationSyntax> members) private static IEnumerable<SyntaxNode> GetChildMemberNodes(SyntaxNode container)
{ {
foreach (var member in members) if (container is CompilationUnitSyntax)
{
if (member is BaseFieldDeclarationSyntax)
{ {
foreach (var declarator in ((BaseFieldDeclarationSyntax)member).Declaration.Variables) foreach (var member in ((CompilationUnitSyntax)container).Members)
{ {
yield return declarator; yield return member;
} }
} }
else else if (container is NamespaceDeclarationSyntax)
{ {
if (IsNameableNode(member)) foreach (var member in ((NamespaceDeclarationSyntax)container).Members)
{ {
yield return member; yield return member;
} }
} }
else if (container is TypeDeclarationSyntax)
{
foreach (var member in ((TypeDeclarationSyntax)container).Members)
{
yield return member;
} }
} }
else if (container is EnumDeclarationSyntax)
private static IEnumerable<SyntaxNode> GetFlattenedMembers(SyntaxNode node)
{ {
switch (node.Kind()) foreach (var member in ((EnumDeclarationSyntax)container).Members)
{ {
case SyntaxKind.CompilationUnit: yield return member;
return GetFlattenedMembers(((CompilationUnitSyntax)node).Members); }
case SyntaxKind.NamespaceDeclaration:
return GetFlattenedMembers(((NamespaceDeclarationSyntax)node).Members);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.StructDeclaration:
return GetFlattenedMembers(((TypeDeclarationSyntax)node).Members);
case SyntaxKind.EnumDeclaration:
return GetFlattenedMembers(((EnumDeclarationSyntax)node).Members);
default:
return SpecializedCollections.EmptyEnumerable<SyntaxNode>();
} }
} }
private static IEnumerable<SyntaxNode> GetNodeAndFlattenedMembers(SyntaxNode node) private static bool NodeIsSupported(bool test, SyntaxNode node)
{
if (IsNameableNode(node))
{ {
yield return node; return !test || IsNameableNode(node);
} }
if (HasMembers(node)) /// <summary>
/// Retrieves the members of a specified <paramref name="container"/> node. The members that are
/// returned can be controlled by passing various parameters.
/// </summary>
/// <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
/// <param name="includeSelf">If true, the container is returned as well.</param>
/// <param name="recursive">If true, members are recursed to return descendent members as well
/// as immediate children. For example, a namespace would return the namespaces and types within.
/// However, if <paramref name="recursive"/> is true, members with the namespaces and types would
/// also be returned.</param>
/// <param name="logicalFields">If true, field declarations are broken into their respecitive declarators.
/// For example, the field "int x, y" would return two declarators, one for x and one for y in place
/// of the field.</param>
/// <param name="onlySupportedNodes">If true, only members supported by Code Model are returned.</param>
public override IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySupportedNodes)
{ {
foreach (var member in GetFlattenedMembers(node).SelectMany(GetNodeAndFlattenedMembers)) if (!IsContainerNode(container))
{ {
yield return member; yield break;
}
}
} }
protected override IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxTree syntaxTree) if (includeSelf && NodeIsSupported(onlySupportedNodes, container))
{ {
return GetNodeAndFlattenedMembers((CompilationUnitSyntax)syntaxTree.GetRoot()); yield return container;
} }
public override IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxNode node) foreach (var member in GetChildMemberNodes(container))
{ {
return GetFlattenedMembers((CSharpSyntaxNode)node); if (member is BaseFieldDeclarationSyntax)
} {
// For fields, the 'logical' and 'supported' flags are intrinsically tied.
// * If 'logical' is true, only declarators should be returned, regardless of the value of 'supported'.
// * If 'logical' is false, the field should only be returned if 'supported' is also false.
protected override IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container) if (logicalFields)
{ {
if (container is CompilationUnitSyntax) foreach (var declarator in ((BaseFieldDeclarationSyntax)member).Declaration.Variables)
{ {
return ((CompilationUnitSyntax)container).Members; // We know that variable declarators are supported, so there's no need to check them here.
yield return declarator;
} }
else if (container is NamespaceDeclarationSyntax)
{
return ((NamespaceDeclarationSyntax)container).Members;
} }
else if (container is TypeDeclarationSyntax) else if (!onlySupportedNodes)
{ {
return ((TypeDeclarationSyntax)container).Members; // Only return field declarations if the supported flag is false.
yield return member;
} }
else if (container is EnumDeclarationSyntax) }
else if (NodeIsSupported(onlySupportedNodes, member))
{ {
return ((EnumDeclarationSyntax)container).Members; yield return member;
} }
return SpecializedCollections.EmptyEnumerable<SyntaxNode>(); if (recursive && IsContainerNode(member))
{
foreach (var innerMember in GetMemberNodes(member, includeSelf: false, recursive: true, logicalFields: logicalFields, onlySupportedNodes: onlySupportedNodes))
{
yield return innerMember;
}
}
}
} }
public override string Language public override string Language
...@@ -3103,7 +3102,7 @@ protected override int GetParameterIndexInContainer(SyntaxNode containerNode, Fu ...@@ -3103,7 +3102,7 @@ protected override int GetParameterIndexInContainer(SyntaxNode containerNode, Fu
protected override int GetMemberIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate) protected override int GetMemberIndexInContainer(SyntaxNode containerNode, Func<SyntaxNode, bool> predicate)
{ {
var members = GetFlattenedMemberNodes(containerNode).ToArray(); var members = GetLogicalMemberNodes(containerNode).ToArray();
int index = 0; int index = 0;
while (index < members.Length) while (index < members.Length)
......
...@@ -110,8 +110,6 @@ protected TextSpan GetEncompassingSpan(SyntaxNode root, SyntaxToken startToken, ...@@ -110,8 +110,6 @@ protected TextSpan GetEncompassingSpan(SyntaxNode root, SyntaxToken startToken,
return TextSpan.FromBounds(startPosition, endPosition); return TextSpan.FromBounds(startPosition, endPosition);
} }
protected abstract IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxTree syntaxTree);
private IBidirectionalMap<SyntaxNodeKey, SyntaxNode> BuildNodeKeyMap(SyntaxTree syntaxTree) private IBidirectionalMap<SyntaxNodeKey, SyntaxNode> BuildNodeKeyMap(SyntaxTree syntaxTree)
{ {
var nameOrdinalMap = new Dictionary<string, int>(); var nameOrdinalMap = new Dictionary<string, int>();
...@@ -194,8 +192,37 @@ public bool TryLookupNode(SyntaxNodeKey nodeKey, SyntaxTree syntaxTree, out Synt ...@@ -194,8 +192,37 @@ public bool TryLookupNode(SyntaxNodeKey nodeKey, SyntaxTree syntaxTree, out Synt
public abstract IEnumerable<SyntaxNode> GetInheritsNodes(SyntaxNode parent); public abstract IEnumerable<SyntaxNode> GetInheritsNodes(SyntaxNode parent);
public abstract IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent); public abstract IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent);
public abstract IEnumerable<SyntaxNode> GetParameterNodes(SyntaxNode parent); public abstract IEnumerable<SyntaxNode> GetParameterNodes(SyntaxNode parent);
public abstract IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxNode node);
protected abstract IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container); protected IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxTree syntaxTree)
{
return GetMemberNodes(syntaxTree.GetRoot(), includeSelf: true, recursive: true, logicalFields: true, onlySupportedNodes: true);
}
protected IEnumerable<SyntaxNode> GetLogicalMemberNodes(SyntaxNode container)
{
return GetMemberNodes(container, includeSelf: false, recursive: false, logicalFields: true, onlySupportedNodes: false);
}
public IEnumerable<SyntaxNode> GetLogicalSupportedMemberNodes(SyntaxNode container)
{
return GetMemberNodes(container, includeSelf: false, recursive: false, logicalFields: true, onlySupportedNodes: true);
}
/// <summary>
/// Retrieves the members of a specified <paramref name="container"/> node. The members that are
/// returned can be controlled by passing various parameters.
/// </summary>
/// <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
/// <param name="includeSelf">If true, the container is returned as well.</param>
/// <param name="recursive">If true, members are recursed to return descendent members as well
/// as immediate children. For example, a namespace would return the namespaces and types within.
/// However, if <paramref name="recursive"/> is true, members with the namespaces and types would
/// also be returned.</param>
/// <param name="logicalFields">If true, field declarations are broken into their respecitive declarators.
/// For example, the field "int x, y" would return two declarators, one for x and one for y in place
/// of the field.</param>
/// <param name="onlySupportedNodes">If true, only members supported by Code Model are returned.</param>
public abstract IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySupportedNodes);
public abstract string Language { get; } public abstract string Language { get; }
public abstract string AssemblyAttributeString { get; } public abstract string AssemblyAttributeString { get; }
...@@ -921,7 +948,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con ...@@ -921,7 +948,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con
containerNode, containerNode,
fileCodeModel, fileCodeModel,
GetMemberIndexInContainer, GetMemberIndexInContainer,
GetMemberNodes); n => GetMemberNodes(n, includeSelf: false, recursive: false, logicalFields: false, onlySupportedNodes: false));
} }
private int PositionVariantToInsertionIndex( private int PositionVariantToInsertionIndex(
...@@ -985,7 +1012,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con ...@@ -985,7 +1012,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con
out TSyntaxNode insertAfterNode) out TSyntaxNode insertAfterNode)
where TSyntaxNode : SyntaxNode where TSyntaxNode : SyntaxNode
{ {
var childNodes = GetFlattenedMemberNodes(containerNode).ToArray(); var childNodes = GetLogicalMemberNodes(containerNode).ToArray();
// Note: childIndexToInsertAfter is 1-based but can be 0, meaning insert before any other members. // Note: childIndexToInsertAfter is 1-based but can be 0, meaning insert before any other members.
// If it isn't 0, it means to insert the member node *after* the node at the 1-based index. // If it isn't 0, it means to insert the member node *after* the node at the 1-based index.
...@@ -1024,7 +1051,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con ...@@ -1024,7 +1051,7 @@ public int PositionVariantToMemberInsertionIndex(object position, SyntaxNode con
private int GetMemberInsertionIndex(SyntaxNode container, int insertionIndex) private int GetMemberInsertionIndex(SyntaxNode container, int insertionIndex)
{ {
var childNodes = GetFlattenedMemberNodes(container).ToArray(); var childNodes = GetLogicalMemberNodes(container).ToArray();
// Note: childIndexToInsertAfter is 1-based but can be 0, meaning insert before any other members. // Note: childIndexToInsertAfter is 1-based but can be 0, meaning insert before any other members.
// If it isn't 0, it means to insert the member node *after* the node at the 1-based index. // If it isn't 0, it means to insert the member node *after* the node at the 1-based index.
...@@ -1037,7 +1064,7 @@ private int GetMemberInsertionIndex(SyntaxNode container, int insertionIndex) ...@@ -1037,7 +1064,7 @@ private int GetMemberInsertionIndex(SyntaxNode container, int insertionIndex)
else else
{ {
var nodeAtIndex = GetFieldFromVariableNode(childNodes[insertionIndex - 1]); var nodeAtIndex = GetFieldFromVariableNode(childNodes[insertionIndex - 1]);
return GetMemberNodes(container).ToList().IndexOf(nodeAtIndex) + 1; return GetMemberNodes(container, includeSelf: false, recursive: false, logicalFields: false, onlySupportedNodes: false).ToList().IndexOf(nodeAtIndex) + 1;
} }
} }
......
...@@ -99,7 +99,7 @@ internal override Snapshot CreateSnapshot() ...@@ -99,7 +99,7 @@ internal override Snapshot CreateSnapshot()
nodesBuilder.AddRange(CodeModelService.GetOptionNodes(node)); nodesBuilder.AddRange(CodeModelService.GetOptionNodes(node));
nodesBuilder.AddRange(CodeModelService.GetImportNodes(node)); nodesBuilder.AddRange(CodeModelService.GetImportNodes(node));
nodesBuilder.AddRange(CodeModelService.GetAttributeNodes(node)); nodesBuilder.AddRange(CodeModelService.GetAttributeNodes(node));
nodesBuilder.AddRange(CodeModelService.GetFlattenedMemberNodes(node)); nodesBuilder.AddRange(CodeModelService.GetLogicalSupportedMemberNodes(node));
return new NodeSnapshot(this.State, _fileCodeModel, node, parentElement, nodesBuilder.ToImmutable()); return new NodeSnapshot(this.State, _fileCodeModel, node, parentElement, nodesBuilder.ToImmutable());
} }
...@@ -150,7 +150,7 @@ protected override bool TryGetItemByIndex(int index, out EnvDTE.CodeElement elem ...@@ -150,7 +150,7 @@ protected override bool TryGetItemByIndex(int index, out EnvDTE.CodeElement elem
currentIndex += attributeNodeCount; currentIndex += attributeNodeCount;
// Members // Members
var memberNodes = CodeModelService.GetFlattenedMemberNodes(node); var memberNodes = CodeModelService.GetLogicalSupportedMemberNodes(node);
var memberNodeCount = memberNodes.Count(); var memberNodeCount = memberNodes.Count();
if (index < currentIndex + memberNodeCount) if (index < currentIndex + memberNodeCount)
{ {
...@@ -208,7 +208,7 @@ protected override bool TryGetItemByName(string name, out EnvDTE.CodeElement ele ...@@ -208,7 +208,7 @@ protected override bool TryGetItemByName(string name, out EnvDTE.CodeElement ele
} }
// Members // Members
foreach (var child in CodeModelService.GetFlattenedMemberNodes(node)) foreach (var child in CodeModelService.GetLogicalSupportedMemberNodes(node))
{ {
var childName = CodeModelService.GetName(child); var childName = CodeModelService.GetName(child);
if (childName == name) if (childName == name)
...@@ -231,7 +231,7 @@ public override int Count ...@@ -231,7 +231,7 @@ public override int Count
CodeModelService.GetOptionNodes(node).Count() + CodeModelService.GetOptionNodes(node).Count() +
CodeModelService.GetImportNodes(node).Count() + CodeModelService.GetImportNodes(node).Count() +
CodeModelService.GetAttributeNodes(node).Count() + CodeModelService.GetAttributeNodes(node).Count() +
CodeModelService.GetFlattenedMemberNodes(node).Count(); CodeModelService.GetLogicalSupportedMemberNodes(node).Count();
} }
} }
} }
......
...@@ -58,7 +58,7 @@ internal override Snapshot CreateSnapshot() ...@@ -58,7 +58,7 @@ internal override Snapshot CreateSnapshot()
var parentElement = (AbstractCodeElement)this.Parent; var parentElement = (AbstractCodeElement)this.Parent;
var nodesBuilder = ImmutableArray.CreateBuilder<SyntaxNode>(); var nodesBuilder = ImmutableArray.CreateBuilder<SyntaxNode>();
nodesBuilder.AddRange(CodeModelService.GetFlattenedMemberNodes(node)); nodesBuilder.AddRange(CodeModelService.GetLogicalSupportedMemberNodes(node));
return new NodeSnapshot(this.State, _fileCodeModel, node, parentElement, nodesBuilder.ToImmutable()); return new NodeSnapshot(this.State, _fileCodeModel, node, parentElement, nodesBuilder.ToImmutable());
} }
...@@ -67,7 +67,7 @@ protected override bool TryGetItemByIndex(int index, out EnvDTE.CodeElement elem ...@@ -67,7 +67,7 @@ protected override bool TryGetItemByIndex(int index, out EnvDTE.CodeElement elem
{ {
var node = LookupNode(); var node = LookupNode();
var memberNodes = CodeModelService.GetFlattenedMemberNodes(node); var memberNodes = CodeModelService.GetLogicalSupportedMemberNodes(node);
if (index >= 0 && index < memberNodes.Count()) if (index >= 0 && index < memberNodes.Count())
{ {
var child = memberNodes.ElementAt(index); var child = memberNodes.ElementAt(index);
...@@ -83,7 +83,7 @@ protected override bool TryGetItemByName(string name, out EnvDTE.CodeElement ele ...@@ -83,7 +83,7 @@ protected override bool TryGetItemByName(string name, out EnvDTE.CodeElement ele
{ {
var node = LookupNode(); var node = LookupNode();
foreach (var child in CodeModelService.GetFlattenedMemberNodes(node)) foreach (var child in CodeModelService.GetLogicalSupportedMemberNodes(node))
{ {
var childName = CodeModelService.GetName(child); var childName = CodeModelService.GetName(child);
if (childName == name) if (childName == name)
...@@ -102,7 +102,7 @@ public override int Count ...@@ -102,7 +102,7 @@ public override int Count
get get
{ {
var node = LookupNode(); var node = LookupNode();
return CodeModelService.GetFlattenedMemberNodes(node).Count(); return CodeModelService.GetLogicalSupportedMemberNodes(node).Count();
} }
} }
} }
......
...@@ -46,11 +46,22 @@ internal interface ICodeModelService : ICodeModelNavigationPointService ...@@ -46,11 +46,22 @@ internal interface ICodeModelService : ICodeModelNavigationPointService
IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent); IEnumerable<SyntaxNode> GetImplementsNodes(SyntaxNode parent);
/// <summary> /// <summary>
/// Retrieves the logical members of a given node, flattening the declarators /// Retrieves the members of a specified <paramref name="container"/> node. The members that are
/// in field declarations. For example, if a class contains the field "int foo, bar", /// returned can be controlled by passing various parameters.
/// two nodes are returned -- one for "foo" and one for "bar".
/// </summary> /// </summary>
IEnumerable<SyntaxNode> GetFlattenedMemberNodes(SyntaxNode parent); /// <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
/// <param name="includeSelf">If true, the container is returned as well.</param>
/// <param name="recursive">If true, members are recursed to return descendent members as well
/// as immediate children. For example, a namespace would return the namespaces and types within.
/// However, if <paramref name="recursive"/> is true, members with the namespaces and types would
/// also be returned.</param>
/// <param name="logicalFields">If true, field declarations are broken into their respecitive declarators.
/// For example, the field "int x, y" would return two declarators, one for x and one for y in place
/// of the field.</param>
/// <param name="onlySuportedNodes">If true, only members supported by Code Model are returned.</param>
IEnumerable<SyntaxNode> GetMemberNodes(SyntaxNode container, bool includeSelf, bool recursive, bool logicalFields, bool onlySuportedNodes);
IEnumerable<SyntaxNode> GetLogicalSupportedMemberNodes(SyntaxNode container);
SyntaxNodeKey GetNodeKey(SyntaxNode node); SyntaxNodeKey GetNodeKey(SyntaxNode node);
SyntaxNodeKey TryGetNodeKey(SyntaxNode node); SyntaxNodeKey TryGetNodeKey(SyntaxNode node);
......
...@@ -1947,6 +1947,33 @@ class C ...@@ -1947,6 +1947,33 @@ class C
TestAddFunction(code, expected, New FunctionData With {.Name = "C", .Kind = EnvDTE.vsCMFunction.vsCMFunctionDestructor, .Type = "void", .Access = EnvDTE.vsCMAccess.vsCMAccessPublic}) TestAddFunction(code, expected, New FunctionData With {.Name = "C", .Kind = EnvDTE.vsCMFunction.vsCMFunctionDestructor, .Type = "void", .Access = EnvDTE.vsCMAccess.vsCMAccessPublic})
End Sub End Sub
<WorkItem(1172038)>
<ConditionalFact(GetType(x86)), Trait(Traits.Feature, Traits.Features.CodeModel)>
Public Sub AddFunction_AfterIncompleteMember()
Dim code =
<Code>
class $$C
{
private void M1()
private void
}
</Code>
Dim expected =
<Code>
class C
{
private void M1()
private void private void M2()
{
}
}
</Code>
TestAddFunction(code, expected, New FunctionData With {.Name = "M2", .Type = "void", .Position = -1, .Access = EnvDTE.vsCMAccess.vsCMAccessPrivate})
End Sub
#End Region #End Region
#Region "AddImplementedInterface tests" #Region "AddImplementedInterface tests"
......
...@@ -853,6 +853,35 @@ End Class ...@@ -853,6 +853,35 @@ End Class
End Sub) End Sub)
End Sub End Sub
<WorkItem(1172038)>
<ConditionalFact(GetType(x86)), Trait(Traits.Feature, Traits.Features.CodeModel)>
Public Sub AddFunction_AfterIncompleteMember()
Dim code =
<Code>
Class $$C
Private Sub M1()
End Sub
Private Sub
End Class
</Code>
Dim expected =
<Code>
Class C
Private Sub M1()
End Sub
Private Sub
Private Sub M2()
End Sub
End Class
</Code>
TestAddFunction(code, expected, New FunctionData With {.Name = "M2", .Type = "void", .Position = -1, .Access = EnvDTE.vsCMAccess.vsCMAccessPrivate})
End Sub
#End Region #End Region
#Region "AddProperty tests" #Region "AddProperty tests"
......
...@@ -418,94 +418,95 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel ...@@ -418,94 +418,95 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel
Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)() Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)()
End Function End Function
Private Shared Function HasMembers(node As SyntaxNode) As Boolean Private Shared Function IsContainerNode(container As SyntaxNode) As Boolean
Select Case node.Kind Return TypeOf container Is CompilationUnitSyntax OrElse
Case SyntaxKind.CompilationUnit, TypeOf container Is NamespaceBlockSyntax OrElse
SyntaxKind.NamespaceBlock, TypeOf container Is TypeBlockSyntax OrElse
SyntaxKind.ClassBlock, TypeOf container Is EnumBlockSyntax
SyntaxKind.StructureBlock,
SyntaxKind.InterfaceBlock,
SyntaxKind.ModuleBlock,
SyntaxKind.EnumBlock
Return True
Case Else
Return False
End Select
End Function End Function
Private Overloads Shared Iterator Function GetFlattenedMemberNodes(members As IEnumerable(Of StatementSyntax)) As IEnumerable(Of SyntaxNode) Private Shared Iterator Function GetChildMemberNodes(container As SyntaxNode) As IEnumerable(Of SyntaxNode)
For Each member In members If TypeOf container Is CompilationUnitSyntax Then
If TypeOf member Is DeclarationStatementSyntax Then For Each member In DirectCast(container, CompilationUnitSyntax).Members
If member.Kind = SyntaxKind.FieldDeclaration Then Yield member
For Each declarator In DirectCast(member, FieldDeclarationSyntax).Declarators
For Each identifier In declarator.Names
Yield identifier
Next Next
ElseIf TypeOf container Is NamespaceBlockSyntax
For Each member In DirectCast(container, NamespaceBlockSyntax).Members
Yield member
Next Next
Else ElseIf TypeOf container Is TypeBlockSyntax
If IsNameableNode(member) Then For Each member In DirectCast(container, TypeBlockSyntax).Members
Yield member Yield member
End If
End If
End If
Next Next
ElseIf TypeOf container Is EnumBlockSyntax
For Each member In DirectCast(container, EnumBlockSyntax).Members
Yield member
Next
End If
End Function End Function
Private Overloads Shared Function GetFlattenedMemberNodesInternal(node As SyntaxNode) As IEnumerable(Of SyntaxNode) Private Shared Function NodeIsSupported(test As Boolean, node As SyntaxNode) As Boolean
Select Case node.Kind Return Not test OrElse IsNameableNode(node)
Case SyntaxKind.CompilationUnit
Return GetFlattenedMemberNodes(DirectCast(node, CompilationUnitSyntax).Members)
Case SyntaxKind.NamespaceBlock
Return GetFlattenedMemberNodes(DirectCast(node, NamespaceBlockSyntax).Members)
Case SyntaxKind.ClassBlock,
SyntaxKind.StructureBlock,
SyntaxKind.InterfaceBlock,
SyntaxKind.ModuleBlock
Return GetFlattenedMemberNodes(DirectCast(node, TypeBlockSyntax).Members)
Case SyntaxKind.EnumBlock
Return GetFlattenedMemberNodes(DirectCast(node, EnumBlockSyntax).Members)
Case Else
Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)()
End Select
End Function End Function
Private Shared Function GetNodeAndFlattenedMembers(node As SyntaxNode) As IEnumerable(Of SyntaxNode)
Dim list = New List(Of SyntaxNode)
If IsNameableNode(node) Then ''' <summary>
list.Add(node) ''' Retrieves the members of a specified <paramref name="container"/> node. The members that are
''' returned can be controlled by passing various parameters.
''' </summary>
''' <param name="container">The <see cref="SyntaxNode"/> from which to retrieve members.</param>
''' <param name="includeSelf">If true, the container Is returned as well.</param>
''' <param name="recursive">If true, members are recursed to return descendent members as well
''' as immediate children. For example, a namespace would return the namespaces And types within.
''' However, if <paramref name="recursive"/> Is true, members with the namespaces And types would
''' also be returned.</param>
''' <param name="logicalFields">If true, field declarations are broken into their respecitive declarators.
''' For example, the field "Dim x, y As Integer" would return two nodes, one for x And one for y in place
''' of the field.</param>
''' <param name="onlySupportedNodes">If true, only members supported by Code Model are returned.</param>
Public Overrides Iterator Function GetMemberNodes(container As SyntaxNode, includeSelf As Boolean, recursive As Boolean, logicalFields As Boolean, onlySupportedNodes As Boolean) As IEnumerable(Of SyntaxNode)
If Not IsContainerNode(container) Then
Exit Function
End If End If
If HasMembers(node) Then If includeSelf AndAlso NodeIsSupported(onlySupportedNodes, container) Then
list.AddRange(GetFlattenedMemberNodesInternal(node).SelectMany(AddressOf GetNodeAndFlattenedMembers)) Yield container
End If End If
Return list For Each member In GetChildMemberNodes(container)
End Function
Protected Overloads Overrides Function GetFlattenedMemberNodes(syntaxTree As SyntaxTree) As IEnumerable(Of SyntaxNode) If member.Kind = SyntaxKind.FieldDeclaration Then
Return GetNodeAndFlattenedMembers(DirectCast(syntaxTree.GetRoot(), CompilationUnitSyntax)) ' For fields, the 'logical' and 'supported' flags are intrinsically tied.
End Function ' * If 'logical' is true, only declarators should be returned, regardless of the value of 'supported'.
' * If 'logical' is false, the field should only be returned if 'supported' is also false.
Public Overloads Overrides Function GetFlattenedMemberNodes(node As SyntaxNode) As IEnumerable(Of SyntaxNode) If logicalFields Then
Return GetFlattenedMemberNodesInternal(node)
End Function
Protected Overrides Function GetMemberNodes(container As SyntaxNode) As IEnumerable(Of SyntaxNode) For Each declarator In DirectCast(member, FieldDeclarationSyntax).Declarators
If TypeOf container Is CompilationUnitSyntax Then
Return DirectCast(container, CompilationUnitSyntax).Members ' We know that declarators are supported, so there's no need to check them here.
ElseIf TypeOf container Is NamespaceBlockSyntax Then For Each identifier In declarator.Names
Return DirectCast(container, NamespaceBlockSyntax).Members Yield identifier
ElseIf TypeOf container Is TypeBlockSyntax Then Next
Return DirectCast(container, TypeBlockSyntax).Members Next
ElseIf TypeOf container Is EnumBlockSyntax Then
Return DirectCast(container, EnumBlockSyntax).Members ElseIf Not onlySupportedNodes
' Only return fields if the supported flag Is false.
Yield member
End If End If
Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)() ElseIf NodeIsSupported(onlySupportedNodes, member)
End Function Yield member
End If
If recursive AndAlso IsContainerNode(member) Then
For Each innerMember In GetMemberNodes(member, includeSelf:=False, recursive:=True, logicalFields:=logicalFields, onlySupportedNodes:=onlySupportedNodes)
Yield innerMember
Next
End If
Next
End Function
Public Overrides ReadOnly Property Language As String Public Overrides ReadOnly Property Language As String
Get Get
...@@ -1575,7 +1576,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel ...@@ -1575,7 +1576,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.CodeModel
End Function End Function
Protected Overrides Function GetMemberIndexInContainer(containerNode As SyntaxNode, predicate As Func(Of SyntaxNode, Boolean)) As Integer Protected Overrides Function GetMemberIndexInContainer(containerNode As SyntaxNode, predicate As Func(Of SyntaxNode, Boolean)) As Integer
Dim members = GetFlattenedMemberNodes(containerNode).ToArray() Dim members = GetLogicalMemberNodes(containerNode).ToArray()
Dim index = 0 Dim index = 0
While index < members.Length While index < members.Length
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册