Files
mod_editor_tools/src/Services/ScenarioSyntaxService.cs
T
2026-10-16 16:18:22 +00:00

129 lines
5.2 KiB
C#

using System.Collections.Generic;
using Cthangover.Mods.Dialog.Scenarios;
using Cthangover.Core.Scenarios;
using Cthangover.Core.Factories.Impls;
using Cthangover.Core.UI.Theming;
using Godot;
namespace Cthangover.Tools.Services
{
/// <summary>
/// Static service providing scenario DSL syntax highlighting, tokenisation,
/// and command-line parsing. <see cref="CreateHighlighter"/> produces a
/// <see cref="CodeHighlighter"/> for the scenario editor's
/// <see cref="TextEdit"/>. <see cref="ExtractCommandParams"/> tokenises a
/// scenario command line into a command name, positional arguments, and
/// named (<c>key=value</c>) parameters — used by
/// <see cref="ScenarioFileService.ExtractReferences"/> for reference analysis.
/// </summary>
public static class ScenarioSyntaxService
{
/// <summary>
/// Builds a <see cref="CodeHighlighter"/> for scenario DSL syntax.
/// Comments (lines starting with <c>#</c>) are green, strings are bright
/// gold, command names are blue, and argument keywords (e.g. <c>key</c>,
/// <c>wait</c>, <c>quest_id</c>) are amber — all from <see cref="UiTokens"/>.
/// </summary>
public static CodeHighlighter CreateHighlighter()
{
var hl = new CodeHighlighter();
hl.AddColorRegion("#", null, UiTokens.Success, true);
hl.AddColorRegion("\"", "\"", UiTokens.AccentBright, false);
hl.NumberColor = UiTokens.Info;
hl.SymbolColor = UiTokens.Muted;
var cmdColor = UiTokens.Info;
hl.KeywordColors = new Godot.Collections.Dictionary();
foreach (var cmd in ScenarioCommandStrategyFactory.GetAllCommandNames())
hl.KeywordColors[cmd] = cmdColor;
foreach (var cmd in new[] {
"option", "select"
}) hl.KeywordColors[cmd] = cmdColor;
var argColor = UiTokens.Accent;
foreach (var kw in new[] {
"key", "first", "second", "hide_color", "wait", "speed", "hidden",
"sprites", "loop", "target", "quest_id", "show", "hide", "duration"
}) hl.KeywordColors[kw] = argColor;
return hl;
}
/// <summary>
/// Splits a line into tokens, handling quoted strings (with backslash
/// escaping) and whitespace. Returns all non-whitespace tokens in order.
/// </summary>
public static List<string> Tokenize(string line)
{
var result = new List<string>();
var i = 0;
while (i < line.Length)
{
if (char.IsWhiteSpace(line[i])) { i++; continue; }
if (line[i] == '"')
{
i++;
var start = i;
while (i < line.Length && line[i] != '"')
{
if (line[i] == '\\' && i + 1 < line.Length) i++;
i++;
}
result.Add(line.Substring(start, i - start));
if (i < line.Length) i++;
continue;
}
var tok = i;
while (i < line.Length && !char.IsWhiteSpace(line[i])) i++;
result.Add(line.Substring(tok, i - tok));
}
return result;
}
/// <summary>Strips surrounding double quotes from a value if present.</summary>
public static string StripQuotes(string val)
{
if (val.Length >= 2 && val.StartsWith("\"") && val.EndsWith("\""))
return val.Substring(1, val.Length - 2);
return val;
}
/// <summary>
/// Parses a scenario command line into its components. Tokenises via
/// <see cref="Tokenize"/>, then extracts the first token as the command
/// name (lowercased), remaining bare tokens as positional arguments, and
/// <c>key=value</c> pairs as named arguments. The <c>-></c> arrow token
/// is consumed but discarded.
/// </summary>
/// <param name="line">A single scenario command line (trimmed).</param>
/// <param name="cmd">The command name, lowercased.</param>
/// <param name="positional">Positional arguments in order.</param>
/// <param name="named">Named arguments keyed by lowercased name.</param>
public static void ExtractCommandParams(string line, out string cmd, out List<string> positional, out Dictionary<string, string> named)
{
positional = new List<string>();
named = new Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase);
cmd = "";
var tokens = Tokenize(line);
if (tokens.Count == 0)
return;
cmd = tokens[0].ToLowerInvariant();
for (int t = 1; t < tokens.Count; t++)
{
var tk = tokens[t];
if (tk == "->") { t++; continue; }
var eq = tk.IndexOf('=');
if (eq > 0)
named[tk.Substring(0, eq).ToLowerInvariant()] = StripQuotes(tk.Substring(eq + 1));
else
positional.Add(tk);
}
}
}
}