Skip to content

Commit d2dab65

Browse files
committed
Add Range and IsImplicit properties to NodeDebugInfo
1 parent a19a8f2 commit d2dab65

File tree

6 files changed

+98
-6
lines changed

6 files changed

+98
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1111
- Added support for variadic functions.
1212
- Added support for cancelling compilation.
1313
- To enable cancelling a compilation, supply a `CancellationToken` to your `CompilationJob` object. You can request that the compilation be cancelled by cancelling the token. For more information, see [Task Cancellation](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation).
14+
- Added `NodeDebugInfo.Range`, which contains the range in which a node appears in the source code.
15+
- Added `NodeDebugInfo.IsImplicit`, which indicates whether the node was created by the compiler (and does not appear in the source code).
1416

1517
### Changed
1618

YarnSpinner.Compiler/DebugInfo.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ internal static ProjectDebugInfo Combine(params ProjectDebugInfo[] debugInfos)
4343
/// </summary>
4444
public class NodeDebugInfo
4545
{
46-
public NodeDebugInfo(string fileName, string nodeName)
46+
public NodeDebugInfo(string? fileName, string nodeName)
4747
{
4848
this.FileName = fileName;
4949
this.NodeName = nodeName;
@@ -52,12 +52,12 @@ public NodeDebugInfo(string fileName, string nodeName)
5252
/// <summary>
5353
/// Gets or sets the file that this DebugInfo was produced from.
5454
/// </summary>
55-
internal string FileName { get; private set; }
55+
public string? FileName { get; internal set; } = null;
5656

5757
/// <summary>
5858
/// Gets or sets the node that this DebugInfo was produced from.
5959
/// </summary>
60-
internal string NodeName { get; set; }
60+
public string NodeName { get; internal set; }
6161

6262
/// <summary>
6363
/// Gets or sets the mapping of instruction numbers to <see
@@ -66,8 +66,25 @@ public NodeDebugInfo(string fileName, string nodeName)
6666
/// </summary>
6767
internal Dictionary<int, Position> LinePositions { get; set; } = new Dictionary<int, Position>();
6868

69+
/// <summary>
70+
/// The range in the file in which the node appears.
71+
/// </summary>
72+
public Range Range { get; internal set; } = new Range();
73+
6974
internal IReadOnlyDictionary<int, string> Labels => this.instructionLabels;
7075

76+
/// <summary>
77+
/// Gets or sets a value indicating whether this node was created by the
78+
/// compiler.
79+
/// </summary>
80+
/// <remarks>
81+
/// Nodes that exist in a .yarn file have an <see cref="IsImplicit"/>
82+
/// value of <see langword="false"/>. Nodes that are generated by the
83+
/// compiler include smart variable implementations, and node group
84+
/// 'hub' nodes.
85+
/// </remarks>
86+
public bool IsImplicit { get; internal set; } = false;
87+
7188
private Dictionary<int, string> instructionLabels = new Dictionary<int, string>();
7289

7390
private HashSet<int> instructionsThatAreDestinations = new HashSet<int>();
@@ -133,7 +150,7 @@ public struct LineInfo
133150
/// The file name of the source that this intruction was produced
134151
/// from.
135152
/// </summary>
136-
public string FileName;
153+
public string? FileName;
137154

138155
/// <summary>
139156
/// The node name of the source that this intruction was produced

YarnSpinner.Compiler/FileCompiler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,15 @@ internal FileCompilationResult Compile()
136136
public override void EnterNode(YarnSpinnerParser.NodeContext context)
137137
{
138138
this.CurrentNode = new Node();
139-
this.CurrentNodeDebugInfo = new NodeDebugInfo(FileParseResult.Name, "<unknown>");
139+
this.CurrentNodeDebugInfo = new NodeDebugInfo(FileParseResult.Name, "<unknown>")
140+
{
141+
Range = new Range(
142+
context.Start.Line,
143+
context.Start.Column,
144+
context.Stop.Line,
145+
context.Stop.Column + (context.Stop.StopIndex - context.Stop.StartIndex)
146+
)
147+
};
140148
}
141149

142150
/// <summary>

YarnSpinner.Compiler/NodeGroupCompiler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ internal IEnumerable<NodeCompilationResult> CompileNodeGroup()
204204
var hubNodeDebugInfo = new NodeDebugInfo("<generated>", NodeGroupName);
205205
this.CurrentNode = hubNode;
206206
this.CurrentNodeDebugInfo = hubNodeDebugInfo;
207+
this.CurrentNodeDebugInfo.IsImplicit = true;
208+
this.CurrentNodeDebugInfo.FileName = null;
207209

208210
// For each member in the group, add code that registers it as a
209211
// candidate. We'll also keep track of each instruction that

YarnSpinner.Compiler/SmartVariableCompiler.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ public NodeCompilationResult Compile(Declaration decl)
4343
public NodeCompilationResult Compile(string sourceFileName, string nodeName, YarnSpinnerParser.ExpressionContext? expression)
4444
{
4545
this.CurrentNode = new Node();
46-
this.CurrentNodeDebugInfo = new NodeDebugInfo(sourceFileName, nodeName);
46+
this.CurrentNodeDebugInfo = new NodeDebugInfo(sourceFileName, nodeName)
47+
{
48+
IsImplicit = true
49+
};
4750

4851
this.CurrentNode.Name = nodeName;
4952
this.CurrentNode.Headers.Add(new Header { Key = "tags", Value = Program.SmartVariableNodeTag });
@@ -55,6 +58,8 @@ public NodeCompilationResult Compile(string sourceFileName, string nodeName, Yar
5558
// empty node with the appropriate tags.
5659
var codeGenerator = new CodeGenerationVisitor(this);
5760
codeGenerator.Visit(expression);
61+
62+
this.CurrentNodeDebugInfo.Range = new Range(expression.Start.Line, expression.Start.Column, expression.Stop.Line, expression.Stop.Column);
5863
}
5964

6065
return new NodeCompilationResult

YarnSpinner.Tests/LanguageTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,64 @@ string GenerateExampleSource(int expressions)
678678
withCancelling.Elapsed.Should().BeLessThan(withoutCancelling.Elapsed,
679679
"cancelling a compilation should complete sooner than letting the compilation run to completion");
680680
}
681+
682+
[Fact]
683+
public void TestNodeDebugInfoContainsInfo()
684+
{
685+
// Given
686+
var source = @"title: NodeA
687+
---
688+
Line 1
689+
Line 2
690+
<<declare $smartVar = 1+1>>
691+
===
692+
title: NodeB
693+
---
694+
Line 3
695+
===
696+
title: NodeGroup
697+
when: always
698+
---
699+
Line in a node group
700+
===
701+
";
702+
703+
704+
// When
705+
var compilationJob = CompilationJob.CreateFromString("input", source);
706+
var result = Compiler.Compile(compilationJob);
707+
708+
// Then
709+
result.ProjectDebugInfo.Nodes.Should().HaveCount(6);
710+
711+
var nodeAInfo = result.ProjectDebugInfo.GetNodeDebugInfo("NodeA");
712+
var nodeBInfo = result.ProjectDebugInfo.GetNodeDebugInfo("NodeB");
713+
var smartVarInfo = result.ProjectDebugInfo.GetNodeDebugInfo("$smartVar");
714+
var nodeGroupHub = result.ProjectDebugInfo.GetNodeDebugInfo("NodeGroup");
715+
var nodeGroupItem = result.ProjectDebugInfo.Nodes.FirstOrDefault(n => n.NodeName.StartsWith("NodeGroup_"));
716+
var nodeGroupItemCondition = result.ProjectDebugInfo.Nodes.FirstOrDefault(n => n.NodeName.Contains(".Condition."));
717+
718+
nodeAInfo.Should().NotBeNull();
719+
nodeBInfo.Should().NotBeNull();
720+
smartVarInfo.Should().NotBeNull();
721+
nodeGroupHub.Should().NotBeNull();
722+
nodeGroupItem.Should().NotBeNull();
723+
nodeGroupItemCondition.Should().NotBeNull();
724+
725+
new[] { nodeAInfo, nodeBInfo, smartVarInfo }.Should().AllSatisfy(i => i.FileName.Should().Be("input"));
726+
nodeAInfo.Range.Should().BeEquivalentTo<Yarn.Compiler.Range>(new(1, 0, 6, 2));
727+
nodeBInfo.Range.Should().BeEquivalentTo<Yarn.Compiler.Range>(new(7, 0, 10, 2));
728+
smartVarInfo.Range.Should().BeEquivalentTo<Yarn.Compiler.Range>(new(5, 22, 5, 24),
729+
"the position of the smart variable 'node' is defined by the expression");
730+
731+
nodeAInfo.IsImplicit.Should().BeFalse();
732+
nodeBInfo.IsImplicit.Should().BeFalse();
733+
smartVarInfo.IsImplicit.Should().BeTrue();
734+
735+
nodeGroupHub.IsImplicit.Should().BeTrue("node group hubs are created by the compiler and do not appear in the input source code");
736+
nodeGroupItem.IsImplicit.Should().BeFalse("nodes in a node group are present in the input source code");
737+
nodeGroupItemCondition.IsImplicit.Should().BeTrue("node group condition smart variables are created by the compiler");
738+
}
681739
}
682740
}
683741

0 commit comments

Comments
 (0)