Skip to content

Commit 8801dbe

Browse files
committed
Markup diagnostics are now reported by the compiler
Fixes YarnSpinnerTool/IssuesDiscussion#118
1 parent 9287556 commit 8801dbe

6 files changed

Lines changed: 102 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212
- The old tagging approach now exists as `RandomLineTagGenerator`
1313
- A new, more human readable, tagger `DescriptiveLineTagGenerator`
1414
- Multiple line or shadow IDs on a single line now generate a diagnostic
15+
- `ParseStringAndIncludeMarkupDiagnostics` call on the `LineParser`
16+
- this allows access to the diagnostics that are generated by parsing the markup
1517

1618
### Changed
1719

1820
- Fixed an issue where `.yarnproject` files couldn't be loaded if the C# project was trimmed.
1921
- Compiling a `CompilationJob` in type-check-only mode now includes the generated string table in its results.
2022
- When tagging lines can now define what `ILineTagGenerator` you want to use
2123
- defaults to using `RandomLineTagGenerator` if you don't set one
24+
- Compiling a `CompilationJob` now includes diagnostics generated by markup parsing.
2225

2326
### Removed
2427

YarnSpinner.Compiler/Compiler.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,25 @@ public static CompilationResult Compile(CompilationJob compilationJob)
235235
}
236236
}
237237

238+
// we run through each line and parse it looking for any markup
239+
// this way we can send over diagnostics at the same time
240+
// later might want to make this a different compilation job type
241+
// or maybe be a different flow entirely
242+
var lineParser = new Yarn.Markup.LineParser();
243+
foreach (var line in stringTableManager.StringTable)
244+
{
245+
if (line.Value.text == null)
246+
{
247+
continue;
248+
}
249+
var result = lineParser.ParseStringAndIncludeMarkupDiagnostics(line.Value.text, System.Globalization.CultureInfo.InvariantCulture.TwoLetterISOLanguageName);
250+
foreach (var diag in result.diagnostics)
251+
{
252+
diagnostics.Add(DiagnosticDescriptor.MarkupFailedToParse.Create(line.Value.fileName, diag.Message));
253+
}
254+
}
255+
256+
238257
if (compilationJob.CompilationType == CompilationJob.Type.StringsOnly)
239258
{
240259
// Stop at this point
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
name: MarkupFailedToParse
3+
code: YS0063
4+
tags: ["shadow-lines", "line-content", "markup"]
5+
6+
description: Dialogue has malformed or invalid markup.
7+
messageTemplate: "Dialogue has malformed or invalid markup. {0}"
8+
messageValues:
9+
- Markup parse message
10+
11+
summary: |
12+
Markup has it's own specific syntax. The line of dialogue has malformed, invalid, unbalanced, or incomplete markup.
13+
14+
defaultSeverity: warning
15+
minimumSeverity: none
16+
---
17+

YarnSpinner.Tests/ErrorHandlingTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,5 +1300,19 @@ public void TestDiagnosticsCanHaveOverriddenSeverities(Diagnostic.DiagnosticSeve
13001300

13011301
diag.Severity.Should().Be(severity);
13021302
}
1303+
1304+
1305+
[Fact]
1306+
public void TestCompilerGeneratesMarkupDiagnostics()
1307+
{
1308+
// this is just checking a single diag comes over
1309+
// ensuring the right diags are generated is over in the markuptests
1310+
var source = CreateTestNode("A line with [a]invalid markup");
1311+
var job = CompilationJob.CreateFromString("<input>", source);
1312+
job.CompilationType = CompilationJob.Type.StringsOnly;
1313+
1314+
var result = Compiler.Compile(job);
1315+
var diag = result.Diagnostics.Should().ContainSingle(d => d.Code == DiagnosticDescriptor.MarkupFailedToParse.Code);
1316+
}
13031317
}
13041318
}

YarnSpinner.Tests/MarkupTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,38 @@ public void TestMarkerProcessorsCanProcessCharacterNames()
15791579
kv => kv.Key == "name" && kv.Value.StringValue == "Mae", "the marker's properties should be unmodified"
15801580
);
15811581
}
1582+
1583+
[Fact]
1584+
public void TestUnderscoreCanBeIdentifiers()
1585+
{
1586+
var lineParser = new LineParser();
1587+
1588+
// checking self-closing tags can have underscores in their name
1589+
var markup = lineParser.ParseString("Narrator: Self-closing tag [under_tag /]with an underscore.", "en-AU");
1590+
markup.Text.Should().Be("Narrator: Self-closing tag with an underscore.");
1591+
1592+
markup.Attributes.Should().HaveCount(2);
1593+
markup.Attributes.Should().Contain(m => m.Name == "under_tag");
1594+
markup.Attributes.Should().Contain(m => m.Name == "character")
1595+
.Which.Properties.Should().Contain(kv => kv.Key == "name" && kv.Value.StringValue == "Narrator");
1596+
1597+
// checking regular markup can have underscores in their name
1598+
markup = lineParser.ParseString("Narrator: This is a [under_tag]regular markup[/under_tag] with underscores", "en-AU");
1599+
markup.Text.Should().Be("Narrator: This is a regular markup with underscores");
1600+
1601+
markup.Attributes.Should().HaveCount(2);
1602+
markup.Attributes.Should().Contain(m => m.Name == "under_tag");
1603+
markup.Attributes.Should().Contain(m => m.Name == "character")
1604+
.Which.Properties.Should().Contain(kv => kv.Key == "name" && kv.Value.StringValue == "Narrator");
1605+
1606+
// checking markup can have properties with underscores
1607+
markup = lineParser.ParseString("Line with a regular [under_tag under_property=\"hello\"]underscored tag with an underscored property also[/under_tag] in it.", "en-AU");
1608+
markup.Text.Should().Be("Line with a regular underscored tag with an underscored property also in it.");
1609+
1610+
markup.Attributes.Should().HaveCount(1);
1611+
markup.Attributes.Should().Contain(m => m.Name == "under_tag")
1612+
.Which.Properties.Should().Contain(kv => kv.Key == "under_property" && kv.Value.StringValue == "hello");
1613+
}
15821614
}
15831615

15841616
public class BBCodeChevronReplacer : IAttributeMarkerProcessor

YarnSpinner/YarnSpinner.Markup/LineParser.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,12 @@ internal bool ComparePattern(LexerTokenTypes[] pattern)
298298

299299
internal List<LexerToken> LexMarkup(string input)
300300
{
301+
HashSet<char> allowedIdentifierPunctuation = new()
302+
{
303+
'_',
304+
'|'
305+
};
306+
301307
List<LexerToken> tokens = new List<LexerToken>();
302308
if (string.IsNullOrEmpty(input))
303309
{
@@ -433,12 +439,14 @@ internal List<LexerToken> LexMarkup(string input)
433439
{
434440
var start = currentPosition;
435441

436-
// keep reading characters until the NEXT character is not a letter or digit
442+
// keep reading characters until the NEXT character is not a letter or digit or allowed punctuation symbol
437443
// when that happens we will stop at that point, emit an id token
438-
while (char.IsLetterOrDigit((char)this.stringReader.Peek()))
444+
char peek = (char)this.stringReader.Peek();
445+
while (char.IsLetterOrDigit(peek) || allowedIdentifierPunctuation.Contains(peek))
439446
{
440447
_ = this.stringReader.Read();
441448
currentPosition += 1;
449+
peek = (char)this.stringReader.Peek();
442450
}
443451

444452
last = new LexerToken(LexerTokenTypes.Identifier)
@@ -1467,6 +1475,13 @@ public MarkupParseResult ParseString(string input, string localeCode, bool addIm
14671475
{
14681476
return ParseString(input, localeCode, squish: true, sort: true, addImplicitCharacterAttribute);
14691477
}
1478+
1479+
/// <returns>A markup parse result and a collection of diagnostics encountered while parsing the markup.</returns>
1480+
/// <inheritdoc cref="ParseString(string, string, bool, bool, bool)" />
1481+
public (MarkupParseResult markup, List<MarkupDiagnostic> diagnostics) ParseStringAndIncludeMarkupDiagnostics(string input, string localeCode, bool addImplicitCharacterAttribute = true)
1482+
{
1483+
return ParseStringWithDiagnostics(input, localeCode, squish: true, sort: true, addImplicitCharacterAttribute);
1484+
}
14701485

14711486
/// <summary>
14721487
/// Parses a string of text and produces a markup parse result.

0 commit comments

Comments
 (0)