diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbdba8..daba532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## v1.6.2.0 - 2026-07-11 +### Added +- Blank lines and indentation in the multiline editor are now kept when you save, so you can space out and indent longer aliases to make them easier to read +- Indented lines are now colored correctly in the editor + +### Changed +- The alias editor now lists every problem with an alias at once, each with its line number, instead of stopping at the first one it finds +- Problems are now colored by how serious they are, so warnings stand out from errors +- A cycle warning (an alias that would trigger itself) now shows alongside the other problems, instead of only appearing when you hovered over the trigger box + ## v1.6.1.0 - 2026-07-09 ### Added - Time variables: your aliases can now react to the clock. `{time}` shows your local time (like 3:45 PM), `{time24}` shows it in 24-hour form (15:45), and `{date}`, `{day}`, `{hour}`, and `{minute}` fill in the rest. `{daypart}` gives morning, afternoon, evening, or night, so a venue greeter can open with something like `Good {daypart}, welcome in!` diff --git a/Silkstring/Services/AliasValidator.cs b/Silkstring/Services/AliasValidator.cs index 0f34e3d..2b788be 100644 --- a/Silkstring/Services/AliasValidator.cs +++ b/Silkstring/Services/AliasValidator.cs @@ -58,7 +58,7 @@ public static IEnumerable ValidateSets(AliasEntry alias, ISet ValidateWaits(AliasEntry alias) { for (var i = 0; i < alias.Output.Count; i++) { - var (kind, expression) = BlockInterpreter.Classify(alias.Output[i].Command); + var (kind, expression) = BlockInterpreter.Classify(alias.Output[i].Command.Trim()); if (kind != BlockKind.Wait) continue; var (value, _) = BlockInterpreter.ParseSet(expression); if (string.IsNullOrEmpty(value)) { yield return new(":wait needs a duration", i); continue; } @@ -83,7 +83,7 @@ public static IEnumerable ValidateUntils(AliasEntry alias, bool allo { for (var i = 0; i < alias.Output.Count; i++) { - var (kind, expression) = BlockInterpreter.Classify(alias.Output[i].Command); + var (kind, expression) = BlockInterpreter.Classify(alias.Output[i].Command.Trim()); if (kind != BlockKind.Until) continue; var (isUnsafe, condition) = BlockInterpreter.ParseUntil(expression); if (string.IsNullOrWhiteSpace(condition)) { yield return new(":until needs a condition", i); continue; } diff --git a/Silkstring/UI/Panels/AliasEditPanel.cs b/Silkstring/UI/Panels/AliasEditPanel.cs index 5d84721..3cd6025 100644 --- a/Silkstring/UI/Panels/AliasEditPanel.cs +++ b/Silkstring/UI/Panels/AliasEditPanel.cs @@ -163,7 +163,7 @@ private void RefreshDiagnostics() private static void ApplyMultiline(AliasEntry alias, string text) { - var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var lines = text.Split('\n'); for (var i = 0; i < lines.Length; i++) { diff --git a/Silkstring/UI/SilkstringHighlighter.cs b/Silkstring/UI/SilkstringHighlighter.cs index edc15ce..1893626 100644 --- a/Silkstring/UI/SilkstringHighlighter.cs +++ b/Silkstring/UI/SilkstringHighlighter.cs @@ -37,25 +37,26 @@ public object Colorize(Span line, object? state) line[i] = new Glyph(line[i].Char, PaletteIndex.Default); } var text = new string(chars); + var indent = text.Length - text.TrimStart().Length; + var body = text.TrimStart(); - var (kind, expression) = BlockInterpreter.Classify(text); - var exprStart = text.Length - expression.Length; - + var (kind, expression) = BlockInterpreter.Classify(body); + var exprStart = indent + body.Length - expression.Length; switch (kind) { case BlockKind.If: - Paint(line, 0, exprStart, PaletteIndex.Keyword); + Paint(line, indent, exprStart, PaletteIndex.Keyword); if (TryParseCondition(expression)) PaintContent(line, text, exprStart); else Paint(line, exprStart, text.Length, Error); break; case BlockKind.Else: case BlockKind.EndIf: - Paint(line, 0, exprStart, PaletteIndex.Keyword); + Paint(line, indent, exprStart, PaletteIndex.Keyword); break; case BlockKind.Set: - Paint(line, 0, exprStart, PaletteIndex.Keyword); + Paint(line, indent, exprStart, PaletteIndex.Keyword); var (name, _) = BlockInterpreter.ParseSet(expression); if (name.Length == 0 || !_definedVariables().Contains(name)) Paint(line, exprStart, exprStart + Math.Max(name.Length, 1), Error); @@ -63,35 +64,35 @@ public object Colorize(Span line, object? state) break; case BlockKind.Wait: - Paint(line, 0, exprStart, PaletteIndex.Keyword); + Paint(line, indent, exprStart, PaletteIndex.Keyword); if (!expression.Contains('{') && !BlockInterpreter.TryParseDuration(expression.Trim(), out _)) Paint(line, exprStart, text.Length, Error); else PaintContent(line, text, exprStart); break; case BlockKind.Until: - Paint(line, 0, exprStart, PaletteIndex.Keyword); + Paint(line, indent, exprStart, PaletteIndex.Keyword); var (_, untilCond) = BlockInterpreter.ParseUntil(expression); if (TryParseCondition(untilCond)) PaintContent(line, text, exprStart); else Paint(line, exprStart, text.Length, Error); break; default: - if (text.Length > 1 && text[0] == ':' && char.IsLetter(text[1])) + if (body.Length > 1 && body[0] == ':' && char.IsLetter(body[1])) { - var kw = text.IndexOf(' '); - if (kw < 0) kw = text.Length; - Paint(line, 0, kw, Error); - PaintContent(line, text, kw); + var kw = body.IndexOf(' '); + if (kw < 0) kw = body.Length; + Paint(line, indent, indent + kw, Error); + PaintContent(line, text, indent + kw); } else { - if (text.StartsWith('/')) + if (body.StartsWith('/')) { - var end = text.IndexOf(' '); - Paint(line, 0, end < 0 ? text.Length : end, PaletteIndex.KnownIdentifier); + var end = body.IndexOf(' '); + Paint(line, indent, indent + (end < 0 ? body.Length : end), PaletteIndex.KnownIdentifier); } - PaintContent(line, text, 0); + PaintContent(line, text, indent); } break; }