Skip to content

Commit 372584f

Browse files
committed
Allow listeners to skip listening to children.
1 parent 9d1e8fa commit 372584f

4 files changed

Lines changed: 62 additions & 8 deletions

File tree

src/MrKWatkins.Ast.Tests/Listening/ListenerTests.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace MrKWatkins.Ast.Tests.Listening;
66
public sealed class ListenerTests : TreeTestFixture
77
{
88
[TestCase(typeof(TestListener), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")]
9-
[TestCase(typeof(CallsBaseTestListener), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")] // Explicitly test the base methods do nothing.
9+
[TestCase(typeof(CallsBaseTestListener), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")] // Explicitly test the base methods do nothing.
1010
[TestCase(typeof(TypedTestListener), "(N1(N11(N111))(N121))")]
1111
[TestCase(typeof(CallsBaseTypedTestListener), "(N1(N11(N111))(N121))")]
1212
public void Listen(Type listenerType, string expected)
@@ -18,16 +18,33 @@ public void Listen(Type listenerType, string expected)
1818
listener.ToString().Should().Be(expected);
1919
}
2020

21+
[Test]
22+
public void ShouldListenToChildren()
23+
{
24+
var listener = new TestListener { ListenToChildren = node => node is not BNode };
25+
26+
listener.Listen(N1);
27+
28+
listener.ToString().Should().Be("(N1(N11(N111))(N12)(N13))");
29+
}
30+
2131
private sealed class TestListener : Listener<TestNode>
2232
{
2333
private readonly StringBuilder stringBuilder = new();
2434

35+
public Func<TestNode, bool>? ListenToChildren { get; init; }
36+
2537
protected internal override void BeforeListenToNode(TestNode node) => stringBuilder.Append('(');
2638

2739
protected internal override void ListenToNode(TestNode node) => stringBuilder.Append(node.Name);
2840

2941
protected internal override void AfterListenToNode(TestNode node) => stringBuilder.Append(')');
3042

43+
protected override bool ShouldListenToChildren(TestNode node)
44+
{
45+
return ListenToChildren?.Invoke(node) ?? base.ShouldListenToChildren(node);
46+
}
47+
3148
public override string ToString() => stringBuilder.ToString();
3249
}
3350

src/MrKWatkins.Ast.Tests/Listening/ListenerWithContextTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace MrKWatkins.Ast.Tests.Listening;
66
public sealed class ListenerWithContextTests : TreeTestFixture
77
{
88
[TestCase(typeof(TestListenerWithContext), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")]
9-
[TestCase(typeof(CallsBaseTestListenerWithContext), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")] // Explicitly test the base methods do nothing.
9+
[TestCase(typeof(CallsBaseTestListenerWithContext), "(N1(N11(N111))(N12(N121)(N122)(N123))(N13))")] // Explicitly test the base methods do nothing.
1010
[TestCase(typeof(TypedTestListenerWithContext), "(N1(N11(N111))(N121))")]
1111
[TestCase(typeof(CallsBaseTypedTestListenerWithContext), "(N1(N11(N111))(N121))")]
1212
public void Listen(Type listenerType, string expected)
@@ -20,13 +20,32 @@ public void Listen(Type listenerType, string expected)
2020
context.ToString().Should().Be(expected);
2121
}
2222

23+
[Test]
24+
public void ShouldListenToChildren()
25+
{
26+
var listener = new TestListenerWithContext { ListenToChildren = (_, node) => node is not BNode };
27+
28+
var context = new StringBuilder();
29+
30+
listener.Listen(context, N1);
31+
32+
context.ToString().Should().Be("(N1(N11(N111))(N12)(N13))");
33+
}
34+
2335
private sealed class TestListenerWithContext : ListenerWithContext<StringBuilder, TestNode>
2436
{
37+
public Func<StringBuilder, TestNode, bool>? ListenToChildren { get; init; }
38+
2539
protected internal override void BeforeListenToNode(StringBuilder context, TestNode node) => context.Append('(');
2640

2741
protected internal override void ListenToNode(StringBuilder context, TestNode node) => context.Append(node.Name);
2842

2943
protected internal override void AfterListenToNode(StringBuilder context, TestNode node) => context.Append(')');
44+
45+
protected override bool ShouldListenToChildren(StringBuilder context, TestNode node)
46+
{
47+
return ListenToChildren?.Invoke(context, node) ?? base.ShouldListenToChildren(context, node);
48+
}
3049
}
3150

3251
private sealed class CallsBaseTestListenerWithContext : ListenerWithContext<StringBuilder, TestNode>

src/MrKWatkins.Ast/Listening/Listener.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public void Listen(TNode node)
2222

2323
ListenToNode(node);
2424

25-
foreach (var child in node.Children)
25+
if (ShouldListenToChildren(node))
2626
{
27-
Listen(child);
27+
foreach (var child in node.Children) Listen(child);
2828
}
2929

3030
AfterListenToNode(node);
@@ -53,6 +53,15 @@ protected internal virtual void ListenToNode(TNode node)
5353
protected internal virtual void AfterListenToNode(TNode node)
5454
{
5555
}
56+
57+
/// <summary>
58+
/// Return a value indicating whether child nodes should be listened to or not. Defaults to <c>true</c>.
59+
/// </summary>
60+
/// <param name="node">The node who's children should be listened to or not.</param>
61+
protected virtual bool ShouldListenToChildren(TNode node)
62+
{
63+
return true;
64+
}
5665
}
5766

5867
/// <summary>

src/MrKWatkins.Ast/Listening/ListenerWithContext.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ public void Listen(TContext context, TNode node)
2424

2525
ListenToNode(context, node);
2626

27-
foreach (var child in node.Children)
28-
{
29-
Listen(context, child);
30-
}
27+
if (ShouldListenToChildren(context, node))
28+
foreach (var child in node.Children)
29+
Listen(context, child);
3130

3231
AfterListenToNode(context, node);
3332
}
@@ -58,6 +57,16 @@ protected internal virtual void ListenToNode(TContext context, TNode node)
5857
protected internal virtual void AfterListenToNode(TContext context, TNode node)
5958
{
6059
}
60+
61+
/// <summary>
62+
/// Return a value indicating whether child nodes should be listened to or not. Defaults to <c>true</c>.
63+
/// </summary>
64+
/// <param name="context">The context object.</param>
65+
/// <param name="node">The node who's children should be listened to or not.</param>
66+
protected virtual bool ShouldListenToChildren(TContext context, TNode node)
67+
{
68+
return true;
69+
}
6170
}
6271

6372
/// <summary>

0 commit comments

Comments
 (0)