From 5dec9ee365331a93fbfaf8cf92a1549e8dd5bf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Wed, 22 May 2013 08:36:12 +0200 Subject: [PATCH] Added DescendantNodes API. --- ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs | 71 ++++++++++++----- .../CSharp/AstTests.cs | 77 +++++++++++++++++++ .../ICSharpCode.NRefactory.Tests.csproj | 1 + .../TypeSystem/DomRegion.cs | 14 +++- 4 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 ICSharpCode.NRefactory.Tests/CSharp/AstTests.cs diff --git a/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs b/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs index 3fbfd6b25d..76d6d828a7 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs @@ -296,31 +296,64 @@ namespace ICSharpCode.NRefactory.CSharp /// Gets all descendants of this node (excluding this node itself). /// public IEnumerable Descendants { - get { return GetDescendants(false); } + get { return GetDescendantsImpl(false); } } /// /// Gets all descendants of this node (including this node itself). /// public IEnumerable DescendantsAndSelf { - get { return GetDescendants(true); } - } - - IEnumerable GetDescendants(bool includeSelf) - { - if (includeSelf) - yield return this; - Stack nextStack = new Stack(); - nextStack.Push(null); - AstNode pos = firstChild; - while (pos != null) { - if (pos.nextSibling != null) - nextStack.Push(pos.nextSibling); - yield return pos; - if (pos.firstChild != null) - pos = pos.firstChild; - else - pos = nextStack.Pop(); + get { return GetDescendantsImpl(true); } + } + + static bool IsInsideRegion(DomRegion region, AstNode pos) + { + if (region.IsEmpty) + return true; + var nodeRegion = pos.Region; + return region.IntersectsWith(nodeRegion) || region.OverlapsWith(nodeRegion); + } + + public IEnumerable DescendantNodes (Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodes (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(false, region, descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, new DomRegion (), descendIntoChildren); + } + + public IEnumerable DescendantNodesAndSelf (DomRegion region, Func descendIntoChildren = null) + { + return GetDescendantsImpl(true, region, descendIntoChildren); + } + + IEnumerable GetDescendantsImpl(bool includeSelf, DomRegion region = new DomRegion (), Func descendIntoChildren = null) + { + if (includeSelf) { + if (IsInsideRegion (region, this)) + yield return this; + if (descendIntoChildren != null && !descendIntoChildren(this)) + yield break; + } + + Stack descendStack = new Stack(); + descendStack.Push(firstChild); + while (descendStack.Count > 0) { + AstNode cur = descendStack.Pop (); + while (cur != null) { + if (IsInsideRegion(region, cur)) + yield return cur; + if (descendIntoChildren == null || descendIntoChildren(cur)) + descendStack.Push(cur.firstChild); + cur = cur.nextSibling; + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/AstTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/AstTests.cs new file mode 100644 index 0000000000..9cccc58b95 --- /dev/null +++ b/ICSharpCode.NRefactory.Tests/CSharp/AstTests.cs @@ -0,0 +1,77 @@ +// +// AstTests.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using NUnit.Framework; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + [TestFixture] + + public class AstTests + { + [Test] + public void TestDescendants () + { + var tree = SyntaxTree.Parse(@"class Test +{ + void Foo() + { + Call1(); + { + Call2(); + } + Call3(); + } +}"); + var method = tree.GetNodeAt(6, 1); + // Body, Call1, Block, Call2 and Call 3 + Assert.AreEqual(5, method.DescendantNodes().Count(n => n is Statement)); + } + + + [Test] + public void TestDescendantsWithPredicate () + { + var tree = SyntaxTree.Parse(@"class Test +{ + void Foo() + { + Call1(); + { + Call2(); + } + Call3(); + } +}"); + var method = tree.GetNodeAt(6, 1); + // Body, Call1, Block and Call 3 - NOT call2 + var childs = method.DescendantNodes(child => !(child is BlockStatement) || (((BlockStatement)child).Parent is MethodDeclaration)).Where(n => n is Statement).ToList(); + Assert.AreEqual(4, childs.Count); + } + } +} + diff --git a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj index 5befaf55c6..dd4fae08b9 100644 --- a/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj +++ b/ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj @@ -415,6 +415,7 @@ + diff --git a/ICSharpCode.NRefactory/TypeSystem/DomRegion.cs b/ICSharpCode.NRefactory/TypeSystem/DomRegion.cs index 7f48f4f152..62c106c6e1 100644 --- a/ICSharpCode.NRefactory/TypeSystem/DomRegion.cs +++ b/ICSharpCode.NRefactory/TypeSystem/DomRegion.cs @@ -156,7 +156,19 @@ namespace ICSharpCode.NRefactory.TypeSystem { return IsInside(location.Line, location.Column); } - + + public bool IntersectsWith (DomRegion region) + { + return region.Begin <= End && region.End >= Begin; + } + + public bool OverlapsWith (DomRegion region) + { + var maxBegin = Begin > region.Begin ? Begin : region.Begin; + var minEnd = End < region.End ? End : region.End; + return maxBegin < minEnd; + } + public override string ToString() { return string.Format(