diff --git a/Src/Compilers/Core/Source/CodeAnalysis.csproj b/Src/Compilers/Core/Source/CodeAnalysis.csproj index 3e47e4917386ca3c9dffd092b1a0a76802c50a53..b33778abd88c075d6ccf758da084ac760e4850a8 100644 --- a/Src/Compilers/Core/Source/CodeAnalysis.csproj +++ b/Src/Compilers/Core/Source/CodeAnalysis.csproj @@ -268,8 +268,6 @@ - - @@ -541,6 +539,7 @@ + diff --git a/Src/Compilers/Core/Source/InternalUtilities/UnionStack`2.cs b/Src/Compilers/Core/Source/InternalUtilities/UnionStack`2.cs deleted file mode 100644 index 38dba693ce234653ca9732ee756a9a263d7898ff..0000000000000000000000000000000000000000 --- a/Src/Compilers/Core/Source/InternalUtilities/UnionStack`2.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. 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.Diagnostics; -using Microsoft.CodeAnalysis; - -namespace Roslyn.Utilities -{ - internal class UnionStack - where T0 : struct - where T1 : struct - { - private ArrayBuilder stack0 = ArrayBuilder.GetInstance(); - private ArrayBuilder stack1 = ArrayBuilder.GetInstance(); - private ArrayBuilder discriminatorStack = ArrayBuilder.GetInstance(); - - public enum Discriminator - { - Invalid, - Value0, - Value1 - } - - public void Push(T0 value) - { - stack0.Push(value); - discriminatorStack.Push(Discriminator.Value0); - } - - public void Push(ref T0 value) - { - stack0.Push(value); - discriminatorStack.Push(Discriminator.Value0); - } - - public void Push(T1 value) - { - stack1.Push(value); - discriminatorStack.Push(Discriminator.Value1); - } - - public void Push(ref T1 value) - { - stack1.Push(value); - discriminatorStack.Push(Discriminator.Value1); - } - - public bool TryPeek(out Discriminator discriminator) - { - if (discriminatorStack.Count == 0) - { - discriminator = Discriminator.Invalid; - return false; - } - - discriminator = discriminatorStack.Peek(); - return true; - } - - public T0 PopValue0() - { - var discriminator = discriminatorStack.Pop(); - Debug.Assert(discriminator == Discriminator.Value0); - return stack0.Pop(); - } - - public T1 PopValue1() - { - var discriminator = discriminatorStack.Pop(); - Debug.Assert(discriminator == Discriminator.Value1); - return stack1.Pop(); - } - - public void FreePooledObjects() - { - stack0.Free(); - stack1.Free(); - discriminatorStack.Free(); - } - } -} \ No newline at end of file diff --git a/Src/Compilers/Core/Source/InternalUtilities/UnionStack`3.cs b/Src/Compilers/Core/Source/InternalUtilities/UnionStack`3.cs deleted file mode 100644 index 849269c2ea95e4e1bc05350f4bdd147550d4c290..0000000000000000000000000000000000000000 --- a/Src/Compilers/Core/Source/InternalUtilities/UnionStack`3.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. 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.Diagnostics; -using Microsoft.CodeAnalysis; - -namespace Roslyn.Utilities -{ - internal class UnionStack - where T0 : struct - where T1 : struct - where T2 : struct - { - private ArrayBuilder stack0 = ArrayBuilder.GetInstance(); - private ArrayBuilder stack1 = ArrayBuilder.GetInstance(); - private ArrayBuilder stack2 = ArrayBuilder.GetInstance(); - - public enum Discriminator : byte - { - Invalid, - Value0, - Value1, - Value2 - } - - private ArrayBuilder discriminatorStack = ArrayBuilder.GetInstance(); - - public void Push(ref T0 value) - { - stack0.Push(value); - discriminatorStack.Push(Discriminator.Value0); - } - - public void Push(T0 value) - { - stack0.Push(value); - discriminatorStack.Push(Discriminator.Value0); - } - - public void Push(ref T1 value) - { - stack1.Push(value); - discriminatorStack.Push(Discriminator.Value1); - } - - public void Push(T1 value) - { - stack1.Push(value); - discriminatorStack.Push(Discriminator.Value1); - } - - public void Push(ref T2 value) - { - stack2.Push(value); - discriminatorStack.Push(Discriminator.Value2); - } - - public void Push(T2 value) - { - stack2.Push(value); - discriminatorStack.Push(Discriminator.Value2); - } - - public bool TryPeekDiscriminator(out Discriminator discriminator) - { - if (discriminatorStack.Count == 0) - { - discriminator = Discriminator.Invalid; - return false; - } - - discriminator = discriminatorStack.Peek(); - return true; - } - - public T0 PopValue0() - { - var discriminator = discriminatorStack.Pop(); - Debug.Assert(discriminator == Discriminator.Value0); - return stack0.Pop(); - } - - public T1 PopValue1() - { - var discriminator = discriminatorStack.Pop(); - Debug.Assert(discriminator == Discriminator.Value1); - return stack1.Pop(); - } - - public T2 PopValue2() - { - var discriminator = discriminatorStack.Pop(); - Debug.Assert(discriminator == Discriminator.Value2); - return stack2.Pop(); - } - - public void FreePooledObjects() - { - stack0.Free(); - stack1.Free(); - stack2.Free(); - discriminatorStack.Free(); - } - } -} \ No newline at end of file diff --git a/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.Enumerator.cs b/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.Enumerator.cs index a2981151187f81b62f5c7ffdf44ccf0f6429d658..0d62ef189c80827ca565e5dbb00670f67f9caca6 100644 --- a/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.Enumerator.cs +++ b/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.Enumerator.cs @@ -52,6 +52,31 @@ public void Reset() { this.childIndex = -1; } + + internal bool TryMoveNextAndGetCurrent(ref SyntaxNodeOrToken current) + { + if (!MoveNext()) + { + return false; + } + + current = ItemInternal(node, this.childIndex); + return true; + } + + internal SyntaxNode TryMoveNextAndGetCurrentAsNode() + { + while (MoveNext()) + { + var nodeValue = ItemInternalAsNode(node, this.childIndex); + if (nodeValue != null) + { + return nodeValue; + } + } + + return null; + } } private class EnumeratorImpl : IEnumerator diff --git a/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.cs b/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.cs index 25281565d66ea860d04e155a6b5fe664a3c9e8ee..bf6c8a585fa0dc19c506d23cce966ce9d8c28bc2 100644 --- a/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.cs +++ b/Src/Compilers/Core/Source/Syntax/ChildSyntaxList.cs @@ -158,6 +158,54 @@ internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index) return new SyntaxNodeOrToken(node, greenChild, position, index); } + /// + /// internal indexer that does not verify index. + /// Used when caller has already ensured that index is within bounds. + /// + internal static SyntaxNode ItemInternalAsNode(SyntaxNode node, int index) + { + GreenNode greenChild; + var green = node.Green; + var idx = index; + var slotIndex = 0; + + // find a slot that contains the node or its parent list (if node is in a list) + // we will be skipping whole slots here so we will not loop for long + // the max possible number of slots is 11 (TypeDeclarationSyntax) + // and typically much less than that + // + // at the end of this loop we will have + // 1) slot index - slotIdx + // 2) if the slot is a list, node index in the list - idx + while (true) + { + greenChild = green.GetSlot(slotIndex); + if (greenChild != null) + { + int currentOccupancy = Occupancy(greenChild); + if (idx < currentOccupancy) + { + break; + } + + idx -= currentOccupancy; + } + + slotIndex++; + } + + // get node that represents this slot + var red = node.GetNodeSlot(slotIndex); + if (greenChild.IsList && red != null) + { + // it is a red list of nodes (separated or not), most common case + return red.GetNodeSlot(idx); + } + + // this is a single node or token + return red; + } + // for debugging private SyntaxNodeOrToken[] Nodes { diff --git a/Src/Compilers/Core/Source/Syntax/SyntaxNode.Iterators.cs b/Src/Compilers/Core/Source/Syntax/SyntaxNode.Iterators.cs new file mode 100644 index 0000000000000000000000000000000000000000..293a8b0b21fca5588e8c3497656c4cac8f08194f --- /dev/null +++ b/Src/Compilers/Core/Source/Syntax/SyntaxNode.Iterators.cs @@ -0,0 +1,608 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + public abstract partial class SyntaxNode + { + private IEnumerable DescendantNodesImpl(TextSpan span, Func descendIntoChildren, bool descendIntoTrivia, bool includeSelf) + { + return descendIntoTrivia + ? DescendantNodesAndTokensImpl(span, descendIntoChildren, descendIntoTrivia, includeSelf).Where(e => e.IsNode).Select(e => e.AsNode()) + : DescendantNodesOnly(span, descendIntoChildren, includeSelf); + } + + private IEnumerable DescendantNodesAndTokensImpl(TextSpan span, Func descendIntoChildren, bool descendIntoTrivia, bool includeSelf) + { + return descendIntoTrivia + ? DescendantNodesAndTokensIntoTrivia(span, descendIntoChildren, includeSelf) + : DescendantNodesAndTokensOnly(span, descendIntoChildren, includeSelf); + } + + private IEnumerable DescendantTriviaImpl(TextSpan span, Func descendIntoChildren = null, bool descendIntoTrivia = false) + { + return descendIntoTrivia + ? DescendantTriviaIntoTrivia(span, descendIntoChildren) + : DescendantTriviaOnly(span, descendIntoChildren); + } + + private static bool IsInSpan(ref TextSpan span, TextSpan childSpan) + { + return span.OverlapsWith(childSpan) + // special case for zero-width tokens (OverlapsWith never returns true for these) + || (childSpan.Length == 0 && span.IntersectsWith(childSpan)); + } + + private struct ChildSyntaxListEnumeratorStack : IDisposable + { + private static readonly ObjectPool StackPool = new ObjectPool(() => new ChildSyntaxList.Enumerator[16]); + + private ChildSyntaxList.Enumerator[] stack; + private int stackPtr; + + public ChildSyntaxListEnumeratorStack(SyntaxNode startingNode) + { + this.stack = StackPool.Allocate(); + this.stackPtr = 0; + this.stack[0] = startingNode.ChildNodesAndTokens().GetEnumerator(); + } + + public bool IsNotEmpty { get { return stackPtr >= 0; } } + + public bool TryGetNextInSpan(ref /*readonly*/ TextSpan span, out SyntaxNodeOrToken value) + { + value = default(SyntaxNodeOrToken); + + while (stack[stackPtr].TryMoveNextAndGetCurrent(ref value)) + { + if (IsInSpan(ref span, value.FullSpan)) + { + return true; + } + } + + stackPtr--; + return false; + } + + public SyntaxNode TryGetNextAsNodeInSpan(ref /*readonly*/ TextSpan span) + { + SyntaxNode nodeValue; + while ((nodeValue = stack[stackPtr].TryMoveNextAndGetCurrentAsNode()) != null) + { + if (IsInSpan(ref span, nodeValue.FullSpan)) + { + return nodeValue; + } + } + + stackPtr--; + return null; + } + + public void PushChildren(SyntaxNode node) + { + if (++stackPtr >= stack.Length) + { + // Geometric growth + Array.Resize(ref stack, checked(stackPtr * 2)); + } + + stack[stackPtr] = node.ChildNodesAndTokens().GetEnumerator(); + } + + public void PushChildren(SyntaxNode node, Func descendIntoChildren) + { + if (descendIntoChildren == null || descendIntoChildren(node)) + { + PushChildren(node); + } + } + + public void Dispose() + { + // Return only reasonably-sized stacks to the pool. + if (stack.Length < 256) + { + StackPool.Free(stack); + } + } + } + + private struct TriviaListEnumeratorStack : IDisposable + { + private static readonly ObjectPool StackPool = new ObjectPool(() => new SyntaxTriviaList.Enumerator[16]); + + private SyntaxTriviaList.Enumerator[] stack; + private int stackPtr; + + public static TriviaListEnumeratorStack Create() + { + return new TriviaListEnumeratorStack() { stack = StackPool.Allocate(), stackPtr = -1 }; + } + + public bool TryGetNext(out SyntaxTrivia value) + { + value = default(SyntaxTrivia); + + if (stack[stackPtr].MoveNext()) + { + value = stack[stackPtr].Current; + return true; + } + + stackPtr--; + return false; + } + + public void PushLeadingTrivia(ref SyntaxToken token) + { + if (++stackPtr >= stack.Length) + { + // Geometric growth + Array.Resize(ref stack, checked(stackPtr * 2)); + } + + stack[stackPtr] = token.LeadingTrivia.GetEnumerator(); + } + + public void PushTrailingTrivia(ref SyntaxToken token) + { + if (++stackPtr >= stack.Length) + { + // Geometric growth + Array.Resize(ref stack, checked(stackPtr * 2)); + } + + stack[stackPtr] = token.TrailingTrivia.GetEnumerator(); + } + + public void Dispose() + { + // Return only reasonably-sized stacks to the pool. + if (stack.Length < 256) + { + StackPool.Free(stack); + } + } + } + + private struct TwoEnumeratorListStack : IDisposable + { + public enum Which : byte + { + Node, + Trivia + } + + private ChildSyntaxListEnumeratorStack nodeStack; + private TriviaListEnumeratorStack triviaStack; + private readonly ArrayBuilder discriminatorStack; + + public TwoEnumeratorListStack(SyntaxNode startingNode) + { + this.nodeStack = new ChildSyntaxListEnumeratorStack(startingNode); + this.triviaStack = TriviaListEnumeratorStack.Create(); + this.discriminatorStack = ArrayBuilder.GetInstance(); + this.discriminatorStack.Push(Which.Node); + } + + public bool IsNotEmpty { get { return discriminatorStack.Count != 0; } } + + public Which PeekNext() + { + return discriminatorStack.Peek(); + } + + public bool TryGetNextInSpan(ref TextSpan span, out SyntaxNodeOrToken value) + { + if (nodeStack.TryGetNextInSpan(ref span, out value)) + { + return true; + } + + discriminatorStack.Pop(); + return false; + } + + public bool TryGetNext(out SyntaxTrivia value) + { + if(triviaStack.TryGetNext(out value)) + { + return true; + } + + discriminatorStack.Pop(); + return false; + } + + public void PushChildren(SyntaxNode node) + { + nodeStack.PushChildren(node); + discriminatorStack.Push(Which.Node); + } + + public void PushLeadingTrivia(ref SyntaxToken token) + { + triviaStack.PushLeadingTrivia(ref token); + discriminatorStack.Push(Which.Trivia); + } + + public void PushTrailingTrivia(ref SyntaxToken token) + { + triviaStack.PushTrailingTrivia(ref token); + discriminatorStack.Push(Which.Trivia); + } + + public void Dispose() + { + this.nodeStack.Dispose(); + this.triviaStack.Dispose(); + this.discriminatorStack.Free(); + } + } + + private struct ThreeEnumeratorListStack : IDisposable + { + public enum Which : byte + { + Node, + Trivia, + Token + } + + private ChildSyntaxListEnumeratorStack nodeStack; + private TriviaListEnumeratorStack triviaStack; + private readonly ArrayBuilder tokenStack; + private readonly ArrayBuilder discriminatorStack; + + public ThreeEnumeratorListStack(SyntaxNode startingNode) + { + this.nodeStack = new ChildSyntaxListEnumeratorStack(startingNode); + this.triviaStack = TriviaListEnumeratorStack.Create(); + this.tokenStack = ArrayBuilder.GetInstance(); + this.discriminatorStack = ArrayBuilder.GetInstance(); + this.discriminatorStack.Push(Which.Node); + } + + public bool IsNotEmpty { get { return discriminatorStack.Count > 0; } } + + public Which PeekNext() + { + return discriminatorStack.Peek(); + } + + public bool TryGetNextInSpan(ref TextSpan span, out SyntaxNodeOrToken value) + { + if (nodeStack.TryGetNextInSpan(ref span, out value)) + { + return true; + } + + discriminatorStack.Pop(); + return false; + } + + public bool TryGetNext(out SyntaxTrivia value) + { + if (triviaStack.TryGetNext(out value)) + { + return true; + } + + discriminatorStack.Pop(); + return false; + } + + public SyntaxNodeOrToken PopToken() + { + discriminatorStack.Pop(); + return tokenStack.Pop(); + } + + public void PushChildren(SyntaxNode node) + { + nodeStack.PushChildren(node); + discriminatorStack.Push(Which.Node); + } + + public void PushLeadingTrivia(ref SyntaxToken token) + { + triviaStack.PushLeadingTrivia(ref token); + discriminatorStack.Push(Which.Trivia); + } + + public void PushTrailingTrivia(ref SyntaxToken token) + { + triviaStack.PushTrailingTrivia(ref token); + discriminatorStack.Push(Which.Trivia); + } + + public void PushToken(ref SyntaxNodeOrToken value) + { + tokenStack.Push(value); + discriminatorStack.Push(Which.Token); + } + + public void Dispose() + { + this.nodeStack.Dispose(); + this.triviaStack.Dispose(); + this.tokenStack.Free(); + this.discriminatorStack.Free(); + } + + } + + private IEnumerable DescendantNodesOnly(TextSpan span, Func descendIntoChildren, bool includeSelf) + { + if (includeSelf && IsInSpan(ref span, this.FullSpan)) + { + yield return this; + } + + if (descendIntoChildren != null && !descendIntoChildren(this)) + { + yield break; + } + + using (var stack = new ChildSyntaxListEnumeratorStack(this)) + { + while (stack.IsNotEmpty) + { + SyntaxNode nodeValue = stack.TryGetNextAsNodeInSpan(ref span); + if (nodeValue != null) + { + yield return nodeValue; + + stack.PushChildren(nodeValue, descendIntoChildren); + } + } + } + } + + private IEnumerable DescendantNodesAndTokensOnly(TextSpan span, Func descendIntoChildren, bool includeSelf) + { + if (includeSelf && IsInSpan(ref span, this.FullSpan)) + { + yield return this; + } + + if (descendIntoChildren != null && !descendIntoChildren(this)) + { + yield break; + } + + using (var stack = new ChildSyntaxListEnumeratorStack(this)) + { + SyntaxNodeOrToken value; + while (stack.IsNotEmpty) + { + if (stack.TryGetNextInSpan(ref span, out value)) + { + yield return value; + + var nodeValue = value.AsNode(); + if (nodeValue != null) + { + stack.PushChildren(nodeValue, descendIntoChildren); + } + } + } + } + } + + private IEnumerable DescendantNodesAndTokensIntoTrivia(TextSpan span, Func descendIntoChildren, bool includeSelf) + { + if (includeSelf && IsInSpan(ref span, this.FullSpan)) + { + yield return this; + } + + if (descendIntoChildren != null && !descendIntoChildren(this)) + { + yield break; + } + + using (var stack = new ThreeEnumeratorListStack(this)) + { + while (stack.IsNotEmpty) + { + switch (stack.PeekNext()) + { + case ThreeEnumeratorListStack.Which.Node: + SyntaxNodeOrToken value; + if (stack.TryGetNextInSpan(ref span, out value)) + { + if (value.IsNode) + { + // parent nodes come before children (prefix document order) + yield return value; + + var nodeValue = value.AsNode(); + + if (descendIntoChildren == null || descendIntoChildren(nodeValue)) + { + stack.PushChildren(nodeValue); + } + } + else if (value.IsToken) + { + var token = value.AsToken(); + + // only look through trivia if this node has structured trivia + if (token.HasStructuredTrivia) + { + // trailing trivia comes last + if (token.HasTrailingTrivia) + { + stack.PushTrailingTrivia(ref token); + } + + // tokens come between leading and trailing trivia + stack.PushToken(ref value); + + // leading trivia comes first + if (token.HasLeadingTrivia) + { + stack.PushLeadingTrivia(ref token); + } + } + else + { + // no structure trivia, so just yield this token now + yield return value; + } + } + } + + break; + + case ThreeEnumeratorListStack.Which.Trivia: + // yield structure nodes and enumerate their children + SyntaxTrivia trivia; + if (stack.TryGetNext(out trivia)) + { + if (trivia.HasStructure && IsInSpan(ref span, trivia.FullSpan)) + { + var structureNode = trivia.GetStructure(); + + // parent nodes come before children (prefix document order) + yield return structureNode; + + if (descendIntoChildren == null || descendIntoChildren(structureNode)) + { + stack.PushChildren(structureNode); + } + } + } + break; + + case ThreeEnumeratorListStack.Which.Token: + yield return stack.PopToken(); + break; + } + } + } + } + + private IEnumerable DescendantTriviaOnly(TextSpan span, Func descendIntoChildren) + { + if (descendIntoChildren != null && !descendIntoChildren(this)) + { + yield break; + } + + using (var stack = new ChildSyntaxListEnumeratorStack(this)) + { + SyntaxNodeOrToken value; + while (stack.IsNotEmpty) + { + if (stack.TryGetNextInSpan(ref span, out value)) + { + if (value.IsNode) + { + var nodeValue = value.AsNode(); + + stack.PushChildren(nodeValue, descendIntoChildren); + } + else if (value.IsToken) + { + var token = value.AsToken(); + + foreach (var trivia in token.LeadingTrivia) + { + if (IsInSpan(ref span, trivia.FullSpan)) + { + yield return trivia; + } + } + + foreach (var trivia in token.TrailingTrivia) + { + if (IsInSpan(ref span, trivia.FullSpan)) + { + yield return trivia; + } + } + } + } + } + } + } + + private IEnumerable DescendantTriviaIntoTrivia(TextSpan span, Func descendIntoChildren) + { + if (descendIntoChildren != null && !descendIntoChildren(this)) + { + yield break; + } + + using (var stack = new TwoEnumeratorListStack(this)) + { + while (stack.IsNotEmpty) + { + switch (stack.PeekNext()) + { + case TwoEnumeratorListStack.Which.Node: + SyntaxNodeOrToken value; + if (stack.TryGetNextInSpan(ref span, out value)) + { + if (value.IsNode) + { + var nodeValue = value.AsNode(); + if (descendIntoChildren == null || descendIntoChildren(nodeValue)) + { + stack.PushChildren(nodeValue); + } + } + else if (value.IsToken) + { + var token = value.AsToken(); + + if (token.HasTrailingTrivia) + { + stack.PushTrailingTrivia(ref token); + } + + if (token.HasLeadingTrivia) + { + stack.PushLeadingTrivia(ref token); + } + } + } + + break; + + case TwoEnumeratorListStack.Which.Trivia: + // yield structure nodes and enumerate their children + SyntaxTrivia trivia; + if (stack.TryGetNext(out trivia)) + { + if (IsInSpan(ref span, trivia.FullSpan)) + { + yield return trivia; + } + + if (trivia.HasStructure) + { + var structureNode = trivia.GetStructure(); + + if (descendIntoChildren == null || descendIntoChildren(structureNode)) + { + stack.PushChildren(structureNode); + } + } + } + + break; + } + } + } + } + } +} diff --git a/Src/Compilers/Core/Source/Syntax/SyntaxNode.cs b/Src/Compilers/Core/Source/Syntax/SyntaxNode.cs index c2643bddb6bd913b295bd01246ad10dd323cb13b..2b7f32f2493540fd36e69aba687a4f32ef3a9590 100644 --- a/Src/Compilers/Core/Source/Syntax/SyntaxNode.cs +++ b/Src/Compilers/Core/Source/Syntax/SyntaxNode.cs @@ -5,23 +5,18 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { - using BoxedEnumerator = StrongBox; - using Stack2 = UnionStack; - using Stack3 = UnionStack; - /// /// Represents a non-terminal node in the syntax tree. This is the language agnostic equivalent of and . /// [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - public abstract class SyntaxNode + public abstract partial class SyntaxNode { private readonly GreenNode green; private readonly SyntaxNode parent; @@ -766,244 +761,6 @@ public IEnumerable DescendantNodesAndTokensAndSelf(TextSpan s return DescendantNodesAndTokensImpl(span, descendIntoChildren, descendIntoTrivia, includeSelf: true); } - private IEnumerable DescendantNodesImpl(TextSpan span, Func descendIntoChildren, bool descendIntoTrivia, bool includeSelf) - { - return descendIntoTrivia - ? DescendantNodesAndTokensImpl(span, descendIntoChildren, descendIntoTrivia, includeSelf).Where(e => e.IsNode).Select(e => e.AsNode()) - : DescendantNodesOnly(span, descendIntoChildren, includeSelf); - } - - private IEnumerable DescendantNodesAndTokensImpl(TextSpan span, Func descendIntoChildren, bool descendIntoTrivia, bool includeSelf) - { - return descendIntoTrivia - ? DescendantNodesAndTokensIntoTrivia(span, descendIntoChildren, includeSelf) - : DescendantNodesAndTokensOnly(span, descendIntoChildren, includeSelf); - } - - private static bool IsInSpan(TextSpan span, TextSpan childSpan) - { - return childSpan.OverlapsWith(span) - // special case for zero-width tokens (OverlapsWith never returns true for these) - || (childSpan.Length == 0 && childSpan.IntersectsWith(span)); - } - - private IEnumerable DescendantNodesOnly(TextSpan span, Func descendIntoChildren, bool includeSelf) - { - if (includeSelf && IsInSpan(span, this.FullSpan)) - { - yield return this; - } - - if (descendIntoChildren != null && !descendIntoChildren(this)) - { - yield break; - } - - // Keep around the strongboxes we use so that don't have to allocate unnecessarily. Use - // strong boxes so we can mutate the enumerators (which are structs) without having to - // pop them off the stack and push them back on. This saves about 10% of the time when - // enumerating the tokens in a tree. - var boxes = new Queue(); - var stack = new Stack(); - stack.Push(new BoxedEnumerator(this.ChildNodesAndTokens().GetEnumerator())); - - while (stack.Count > 0) - { - var enumerator = stack.Peek(); - if (!enumerator.Value.MoveNext()) - { - var box = stack.Pop(); - box.Value = default(ChildSyntaxList.Enumerator); - boxes.Enqueue(box); - } - else - { - var nodeValue = enumerator.Value.Current.AsNode(); - if (nodeValue != null && IsInSpan(span, nodeValue.FullSpan)) - { - yield return nodeValue; - - if (descendIntoChildren == null || descendIntoChildren(nodeValue)) - { - var box = boxes.Count == 0 - ? new BoxedEnumerator() - : boxes.Dequeue(); - box.Value = nodeValue.ChildNodesAndTokens().GetEnumerator(); - stack.Push(box); - } - } - } - } - } - - private IEnumerable DescendantNodesAndTokensOnly(TextSpan span, Func descendIntoChildren, bool includeSelf) - { - if (includeSelf && IsInSpan(span, this.FullSpan)) - { - yield return this; - } - - if (descendIntoChildren != null && !descendIntoChildren(this)) - { - yield break; - } - - // Keep around the strongboxes we use so that don't have to allocate unnecessarily. Use - // strong boxes so we can mutate the enumerators (which are structs) without having to - // pop them off the stack and push them back on. This saves about 10% of the time when - // enumerating the tokens in a tree. - var boxes = new Queue(); - var stack = new Stack(); - stack.Push(new BoxedEnumerator(this.ChildNodesAndTokens().GetEnumerator())); - - while (stack.Count > 0) - { - var enumerator = stack.Peek(); - if (!enumerator.Value.MoveNext()) - { - var box = stack.Pop(); - box.Value = default(ChildSyntaxList.Enumerator); - boxes.Enqueue(box); - } - else - { - var value = enumerator.Value.Current; - - if (IsInSpan(span, value.FullSpan)) - { - yield return value; - - var nodeValue = value.AsNode(); - if (nodeValue != null) - { - if (descendIntoChildren == null || descendIntoChildren(nodeValue)) - { - var box = boxes.Count == 0 - ? new BoxedEnumerator() - : boxes.Dequeue(); - box.Value = value.AsNode().ChildNodesAndTokens().GetEnumerator(); - stack.Push(box); - } - } - } - } - } - } - - private IEnumerable DescendantNodesAndTokensIntoTrivia(TextSpan span, Func descendIntoChildren, bool includeSelf) - { - if (includeSelf && IsInSpan(span, this.FullSpan)) - { - yield return this; - } - - if (descendIntoChildren != null && !descendIntoChildren(this)) - { - yield break; - } - - var stack = new Stack3(); - try - { - stack.Push(this.ChildNodesAndTokens().GetEnumerator()); - - Stack3.Discriminator discriminator; - while (stack.TryPeekDiscriminator(out discriminator)) - { - switch (discriminator) - { - case Stack3.Discriminator.Value0: // child nodes & tokens - { - var enumerator = stack.PopValue0(); - if (enumerator.MoveNext()) - { - var value = enumerator.Current; - stack.Push(ref enumerator); - - if (IsInSpan(span, value.FullSpan)) - { - if (value.IsNode) - { - // parent nodes come before children (prefix document order) - yield return value; - - var nodeValue = value.AsNode(); - - if (descendIntoChildren == null || descendIntoChildren(nodeValue)) - { - stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator()); - } - } - else if (value.IsToken) - { - var token = value.AsToken(); - - // only look through trivia if this node has structured trivia - if (token.HasStructuredTrivia) - { - // trailing trivia comes last - if (token.HasTrailingTrivia) - { - stack.Push(token.TrailingTrivia.GetEnumerator()); - } - - // tokens come between leading and trailing trivia - stack.Push(ref value); - - // leading trivia comes first - if (token.HasLeadingTrivia) - { - stack.Push(token.LeadingTrivia.GetEnumerator()); - } - } - else - { - // no structure trivia, so just yield this token now - yield return value; - } - } - } - } - break; - } - - case Stack3.Discriminator.Value1: // trivia - { - // yield structure nodes and enumerate their children - var enumerator = stack.PopValue1(); - if (enumerator.MoveNext()) - { - var trivia = enumerator.Current; - stack.Push(ref enumerator); - - if (trivia.HasStructure && IsInSpan(span, trivia.FullSpan)) - { - var structureNode = trivia.GetStructure(); - - // parent nodes come before children (prefix document order) - yield return structureNode; - - if (descendIntoChildren == null || descendIntoChildren(structureNode)) - { - stack.Push(structureNode.ChildNodesAndTokens().GetEnumerator()); - } - } - } - break; - } - - case Stack3.Discriminator.Value2: // single node or token - yield return stack.PopValue2(); - break; - } - } - } - finally - { - stack.FreePooledObjects(); - } - } - /// /// Finds the node with the smallest that contains . /// is used to determine the behavior in case of a tie (i.e. a node having the same span as its parent). @@ -1143,7 +900,7 @@ public SyntaxTrivia FindTrivia(int position, bool findInsideTrivia = false) /// public IEnumerable DescendantTrivia(Func descendIntoChildren = null, bool descendIntoTrivia = false) { - return DescendantTrivia(this.FullSpan, descendIntoChildren, descendIntoTrivia); + return DescendantTriviaImpl(this.FullSpan, descendIntoChildren, descendIntoTrivia); } /// @@ -1151,161 +908,9 @@ public IEnumerable DescendantTrivia(Func descend /// public IEnumerable DescendantTrivia(TextSpan span, Func descendIntoChildren = null, bool descendIntoTrivia = false) { - if (descendIntoTrivia) - { - return DescendantTriviaIntoTrivia(span, descendIntoChildren); - } - else - { - return DescendantTriviaOnly(span, descendIntoChildren); - } + return DescendantTriviaImpl(span, descendIntoChildren, descendIntoTrivia); } - private IEnumerable DescendantTriviaOnly(TextSpan span, Func descendIntoChildren) - { - if (descendIntoChildren != null && !descendIntoChildren(this)) - { - yield break; - } - - var stack = new Stack(); - stack.Push(this.ChildNodesAndTokens().GetEnumerator()); - - while (stack.Count > 0) - { - var enumerator = stack.Pop(); - if (enumerator.MoveNext()) - { - var value = enumerator.Current; - stack.Push(enumerator); - - if (IsInSpan(span, value.FullSpan)) - { - if (value.IsNode) - { - var nodeValue = value.AsNode(); - - if (descendIntoChildren == null || descendIntoChildren(nodeValue)) - { - stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator()); - } - } - else if (value.IsToken) - { - var token = value.AsToken(); - - foreach (var trivia in token.LeadingTrivia) - { - if (IsInSpan(span, trivia.FullSpan)) - { - yield return trivia; - } - } - - foreach (var trivia in token.TrailingTrivia) - { - if (IsInSpan(span, trivia.FullSpan)) - { - yield return trivia; - } - } - } - } - } - } - } - - private IEnumerable DescendantTriviaIntoTrivia(TextSpan span, Func descendIntoChildren) - { - if (descendIntoChildren != null && !descendIntoChildren(this)) - { - yield break; - } - - var stack = new Stack2(); - try - { - stack.Push(this.ChildNodesAndTokens().GetEnumerator()); - - Stack2.Discriminator discriminator; - while (stack.TryPeek(out discriminator)) - { - switch (discriminator) - { - case Stack2.Discriminator.Value0: // child nodes & tokens - { - var enumerator = stack.PopValue0(); - if (enumerator.MoveNext()) - { - var value = enumerator.Current; - stack.Push(ref enumerator); - - if (IsInSpan(span, value.FullSpan)) - { - if (value.IsNode) - { - var nodeValue = value.AsNode(); - - if (descendIntoChildren == null || descendIntoChildren(nodeValue)) - { - stack.Push(value.AsNode().ChildNodesAndTokens().GetEnumerator()); - } - } - else if (value.IsToken) - { - var token = value.AsToken(); - - if (token.HasTrailingTrivia) - { - stack.Push(token.TrailingTrivia.GetEnumerator()); - } - - if (token.HasLeadingTrivia) - { - stack.Push(token.LeadingTrivia.GetEnumerator()); - } - } - } - } - break; - } - - case Stack2.Discriminator.Value1: // trivia - { - var enumerator = stack.PopValue1(); - // yield structure nodes and enumerate their children - if (enumerator.MoveNext()) - { - var trivia = enumerator.Current; - stack.Push(ref enumerator); - - if (IsInSpan(span, trivia.FullSpan)) - { - yield return trivia; - } - - if (trivia.HasStructure) - { - var structureNode = trivia.GetStructure(); - - if (descendIntoChildren == null || descendIntoChildren(structureNode)) - { - stack.Push(structureNode.ChildNodesAndTokens().GetEnumerator()); - } - } - } - break; - } - } - } - } - finally - { - stack.FreePooledObjects(); - } - } - - #endregion #region Annotations