editor first run
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Inspectron.PrintServer.TemplateEditor.Services;
|
||||
|
||||
public record JsonPropertyInfo(string Path, string Type, bool IsArray);
|
||||
|
||||
public static class JsonPathExtractor
|
||||
{
|
||||
public static List<JsonPropertyInfo> Extract(string json)
|
||||
{
|
||||
var results = new List<JsonPropertyInfo>();
|
||||
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
WalkElement(doc.RootElement, "", results);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Invalid JSON - return empty list
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void WalkElement(JsonElement element, string prefix, List<JsonPropertyInfo> results)
|
||||
{
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
foreach (var prop in element.EnumerateObject())
|
||||
{
|
||||
var path = string.IsNullOrEmpty(prefix) ? prop.Name : $"{prefix}.{prop.Name}";
|
||||
|
||||
var type = GetTypeName(prop.Value.ValueKind);
|
||||
var isArray = prop.Value.ValueKind == JsonValueKind.Array;
|
||||
|
||||
results.Add(new JsonPropertyInfo(path, type, isArray));
|
||||
|
||||
if (prop.Value.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
WalkElement(prop.Value, path, results);
|
||||
}
|
||||
else if (prop.Value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
WalkArrayElement(prop.Value, path, results);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WalkArrayElement(JsonElement array, string arrayPath, List<JsonPropertyInfo> results)
|
||||
{
|
||||
// Inspect first element to discover item property paths
|
||||
foreach (var item in array.EnumerateArray())
|
||||
{
|
||||
if (item.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
// Use "[item]" prefix to indicate these are array item properties
|
||||
foreach (var prop in item.EnumerateObject())
|
||||
{
|
||||
var itemPath = $"{arrayPath}[].{prop.Name}";
|
||||
var type = GetTypeName(prop.Value.ValueKind);
|
||||
var isArray = prop.Value.ValueKind == JsonValueKind.Array;
|
||||
|
||||
results.Add(new JsonPropertyInfo(itemPath, type, isArray));
|
||||
|
||||
if (prop.Value.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
WalkElement(prop.Value, itemPath, results);
|
||||
}
|
||||
else if (prop.Value.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
WalkArrayElement(prop.Value, itemPath, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break; // Only inspect the first element
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetTypeName(JsonValueKind kind) => kind switch
|
||||
{
|
||||
JsonValueKind.String => "string",
|
||||
JsonValueKind.Number => "number",
|
||||
JsonValueKind.True or JsonValueKind.False => "boolean",
|
||||
JsonValueKind.Array => "array",
|
||||
JsonValueKind.Object => "object",
|
||||
JsonValueKind.Null => "null",
|
||||
_ => "unknown"
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
using System.Text.Json;
|
||||
using BlazorMonaco;
|
||||
using BlazorMonaco.Languages;
|
||||
|
||||
namespace Inspectron.PrintServer.TemplateEditor.Services;
|
||||
|
||||
public class TemplateCompletionProvider
|
||||
{
|
||||
private List<JsonPropertyInfo> _jsonPaths = [];
|
||||
|
||||
public void SetJsonPaths(List<JsonPropertyInfo> paths) => _jsonPaths = paths;
|
||||
|
||||
public Task<CompletionList> ProvideCompletionItems(string modelUri, Position position, CompletionContext context)
|
||||
{
|
||||
var items = new List<CompletionItem>();
|
||||
var editorContent = _editorContentAccessor?.Invoke();
|
||||
|
||||
if (string.IsNullOrEmpty(editorContent))
|
||||
return Task.FromResult(new CompletionList { Suggestions = items });
|
||||
|
||||
var lines = editorContent.Split('\n');
|
||||
if (position.LineNumber < 1 || position.LineNumber > lines.Length)
|
||||
return Task.FromResult(new CompletionList { Suggestions = items });
|
||||
|
||||
var line = lines[position.LineNumber - 1];
|
||||
var col = Math.Min(position.Column - 1, line.Length);
|
||||
var textBefore = line[..col];
|
||||
|
||||
var completionContext = DetectContext(textBefore, lines, position.LineNumber);
|
||||
|
||||
switch (completionContext.Kind)
|
||||
{
|
||||
case ContextKind.ElementName:
|
||||
items = GetElementCompletions(completionContext.Prefix);
|
||||
break;
|
||||
case ContextKind.AttributeName:
|
||||
items = GetAttributeCompletions(completionContext.ElementName, completionContext.Prefix);
|
||||
break;
|
||||
case ContextKind.AttributeValue:
|
||||
items = GetAttributeValueCompletions(completionContext.ElementName, completionContext.AttributeName);
|
||||
break;
|
||||
case ContextKind.Expression:
|
||||
items = GetExpressionCompletions(completionContext.Prefix, lines, position.LineNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
return Task.FromResult(new CompletionList { Suggestions = items });
|
||||
}
|
||||
|
||||
public Task<CompletionItem> ResolveCompletionItem(CompletionItem item) => Task.FromResult(item);
|
||||
|
||||
private Func<string>? _editorContentAccessor;
|
||||
|
||||
public void SetEditorContentAccessor(Func<string> accessor) => _editorContentAccessor = accessor;
|
||||
|
||||
#region Context Detection
|
||||
|
||||
private enum ContextKind { None, ElementName, AttributeName, AttributeValue, Expression }
|
||||
|
||||
private record CompletionContextInfo(ContextKind Kind, string Prefix = "", string ElementName = "", string AttributeName = "");
|
||||
|
||||
private static CompletionContextInfo DetectContext(string textBefore, string[] lines, int lineNumber)
|
||||
{
|
||||
// Inside {{ }} expression?
|
||||
var lastOpen = textBefore.LastIndexOf("{{", StringComparison.Ordinal);
|
||||
var lastClose = textBefore.LastIndexOf("}}", StringComparison.Ordinal);
|
||||
if (lastOpen >= 0 && lastOpen > lastClose)
|
||||
{
|
||||
var exprText = textBefore[(lastOpen + 2)..];
|
||||
return new CompletionContextInfo(ContextKind.Expression, exprText);
|
||||
}
|
||||
|
||||
// Inside attribute value (="...")?
|
||||
var inAttrValue = IsInsideAttributeValue(textBefore);
|
||||
if (inAttrValue.Inside)
|
||||
return new CompletionContextInfo(ContextKind.AttributeValue,
|
||||
ElementName: inAttrValue.ElementName, AttributeName: inAttrValue.AttributeName);
|
||||
|
||||
// After < for element name?
|
||||
var lastLt = textBefore.LastIndexOf('<');
|
||||
var lastGt = textBefore.LastIndexOf('>');
|
||||
if (lastLt >= 0 && lastLt > lastGt)
|
||||
{
|
||||
var afterLt = textBefore[(lastLt + 1)..];
|
||||
|
||||
// Closing tag </...
|
||||
if (afterLt.StartsWith("/"))
|
||||
{
|
||||
var prefix = afterLt[1..];
|
||||
return new CompletionContextInfo(ContextKind.ElementName, prefix);
|
||||
}
|
||||
|
||||
// Check if we're still on the element name or moved to attributes
|
||||
var spaceIdx = afterLt.IndexOf(' ');
|
||||
if (spaceIdx < 0)
|
||||
{
|
||||
// Still typing element name
|
||||
return new CompletionContextInfo(ContextKind.ElementName, afterLt);
|
||||
}
|
||||
|
||||
// After element name + space = attribute context
|
||||
var elemName = afterLt[..spaceIdx].Trim();
|
||||
var attrText = afterLt[spaceIdx..].TrimStart();
|
||||
// Get the last partial attribute name being typed
|
||||
var lastSpace = attrText.LastIndexOfAny([' ', '"', '\'']);
|
||||
var attrPrefix = lastSpace >= 0 ? attrText[(lastSpace + 1)..] : attrText;
|
||||
// Filter out if we're right after = or inside a value
|
||||
if (!attrPrefix.Contains('='))
|
||||
return new CompletionContextInfo(ContextKind.AttributeName, attrPrefix, elemName);
|
||||
}
|
||||
|
||||
return new CompletionContextInfo(ContextKind.None);
|
||||
}
|
||||
|
||||
private static (bool Inside, string ElementName, string AttributeName) IsInsideAttributeValue(string text)
|
||||
{
|
||||
// Find the last opening tag context
|
||||
var lastLt = text.LastIndexOf('<');
|
||||
if (lastLt < 0) return (false, "", "");
|
||||
|
||||
var tagContent = text[(lastLt + 1)..];
|
||||
var lastGt = tagContent.LastIndexOf('>');
|
||||
if (lastGt >= 0) return (false, "", "");
|
||||
|
||||
// Extract element name
|
||||
var spaceIdx = tagContent.IndexOf(' ');
|
||||
if (spaceIdx < 0) return (false, "", "");
|
||||
var elemName = tagContent[..spaceIdx];
|
||||
if (elemName.StartsWith("/")) return (false, "", "");
|
||||
|
||||
// Check if cursor is inside a quoted attribute value
|
||||
var attrPart = tagContent[spaceIdx..];
|
||||
var inDoubleQuote = false;
|
||||
var inSingleQuote = false;
|
||||
var lastAttrName = "";
|
||||
var i = 0;
|
||||
|
||||
while (i < attrPart.Length)
|
||||
{
|
||||
if (attrPart[i] == '"' && !inSingleQuote)
|
||||
{
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
}
|
||||
else if (attrPart[i] == '\'' && !inDoubleQuote)
|
||||
{
|
||||
inSingleQuote = !inSingleQuote;
|
||||
}
|
||||
else if (!inDoubleQuote && !inSingleQuote && char.IsLetter(attrPart[i]))
|
||||
{
|
||||
// Capture attribute name
|
||||
var start = i;
|
||||
while (i < attrPart.Length && (char.IsLetterOrDigit(attrPart[i]) || attrPart[i] == '-' || attrPart[i] == '_'))
|
||||
i++;
|
||||
lastAttrName = attrPart[start..i];
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (inDoubleQuote || inSingleQuote)
|
||||
return (true, elemName, lastAttrName);
|
||||
|
||||
return (false, "", "");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Element Schema
|
||||
|
||||
private static readonly Dictionary<string, ElementSchema> Schema = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["receipt"] = new("Required root element", []),
|
||||
["line"] = new("Single line of text", new()
|
||||
{
|
||||
["align"] = new(["left", "center", "right"], "Text alignment"),
|
||||
["bold"] = BoolAttr("Bold text"),
|
||||
["big"] = BoolAttr("Double-width font"),
|
||||
["tall"] = BoolAttr("Double-height font"),
|
||||
["red"] = BoolAttr("Red text (supported printers)"),
|
||||
["lineSpacing"] = new([], "Line spacing in dots"),
|
||||
["wrap"] = BoolAttr("Word-wrap text"),
|
||||
["wrapIndent"] = new([], "Indent for wrapped lines"),
|
||||
}),
|
||||
["columns"] = new("Two-column layout", new()
|
||||
{
|
||||
["left"] = new([], "Left column content"),
|
||||
["right"] = new([], "Right column content"),
|
||||
["bold"] = BoolAttr("Bold text"),
|
||||
["big"] = BoolAttr("Double-width font"),
|
||||
["tall"] = BoolAttr("Double-height font"),
|
||||
["red"] = BoolAttr("Red text"),
|
||||
["lineSpacing"] = new([], "Line spacing in dots"),
|
||||
["wrap"] = BoolAttr("Wrap left column"),
|
||||
["wrapIndent"] = new([], "Indent for wrapped lines"),
|
||||
}),
|
||||
["row"] = new("Multi-column layout with <col> children", new()
|
||||
{
|
||||
["bold"] = BoolAttr("Bold text"),
|
||||
["big"] = BoolAttr("Double-width font"),
|
||||
["tall"] = BoolAttr("Double-height font"),
|
||||
["red"] = BoolAttr("Red text"),
|
||||
["lineSpacing"] = new([], "Line spacing in dots"),
|
||||
}),
|
||||
["col"] = new("Column inside <row> or <table>", new()
|
||||
{
|
||||
["width"] = new([], "Column width (char count or percentage)"),
|
||||
["align"] = new(["left", "center", "right"], "Cell alignment"),
|
||||
["wrap"] = BoolAttr("Word-wrap cell text"),
|
||||
}),
|
||||
["separator"] = new("Repeated character line", new()
|
||||
{
|
||||
["char"] = new([], "Character to repeat (default: -)"),
|
||||
}),
|
||||
["cut"] = new("Paper cut command", []),
|
||||
["feed"] = new("Empty lines", new()
|
||||
{
|
||||
["lines"] = new([], "Number of empty lines"),
|
||||
}),
|
||||
["table"] = new("ASCII box table", new()
|
||||
{
|
||||
["items"] = new([], "JSON array path for data rows"),
|
||||
["var"] = new([], "Loop variable name"),
|
||||
["headerItems"] = new([], "JSON array path for header rows"),
|
||||
}),
|
||||
["foreach"] = new("Loop over array", new()
|
||||
{
|
||||
["items"] = new([], "Path to JSON array (required)"),
|
||||
["var"] = new([], "Loop variable name (required)"),
|
||||
}),
|
||||
["if"] = new("Conditional block", new()
|
||||
{
|
||||
["test"] = new([], "Condition expression (required)"),
|
||||
}),
|
||||
["else"] = new("Else branch (must follow <if>)", []),
|
||||
};
|
||||
|
||||
private static AttributeSchema BoolAttr(string desc) => new(["true", "false"], desc);
|
||||
|
||||
private record ElementSchema(string Description, Dictionary<string, AttributeSchema> Attributes);
|
||||
private record AttributeSchema(List<string> Values, string Description);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Completion Generators
|
||||
|
||||
private static List<CompletionItem> GetElementCompletions(string prefix)
|
||||
{
|
||||
var items = new List<CompletionItem>();
|
||||
var sortIndex = 0;
|
||||
|
||||
foreach (var (name, schema) in Schema)
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = name,
|
||||
Kind = CompletionItemKind.Keyword,
|
||||
Detail = schema.Description,
|
||||
InsertText = GetElementSnippet(name, schema),
|
||||
InsertTextRules = CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
SortText = sortIndex++.ToString("D3"),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static string GetElementSnippet(string name, ElementSchema schema)
|
||||
{
|
||||
return name switch
|
||||
{
|
||||
"line" => "line${1: align=\"${2:left}\"}>${3:$0}</line>",
|
||||
"columns" => "columns left=\"${1}\" right=\"${2}\" />",
|
||||
"row" => "row>\n\t<col align=\"${1:left}\">${2}</col>\n\t<col align=\"${3:right}\">${4}</col>\n</row>",
|
||||
"col" => "col${1: align=\"${2:left}\"}>${3:$0}</col>",
|
||||
"separator" => "separator />",
|
||||
"cut" => "cut />",
|
||||
"feed" => "feed${1: lines=\"${2:1}\"} />",
|
||||
"foreach" => "foreach items=\"${1}\" var=\"${2:item}\">\n\t$0\n</foreach>",
|
||||
"if" => "if test=\"${1}\">\n\t$0\n</if>",
|
||||
"else" => "else>\n\t$0\n</else>",
|
||||
"table" => "table items=\"${1}\" var=\"${2:item}\">\n\t<col width=\"${3}\" align=\"${4:left}\">${5:Header}</col>\n</table>",
|
||||
"receipt" => "receipt>\n\t$0\n</receipt>",
|
||||
_ => $"{name}>$0</{name}>"
|
||||
};
|
||||
}
|
||||
|
||||
private static List<CompletionItem> GetAttributeCompletions(string elementName, string prefix)
|
||||
{
|
||||
var items = new List<CompletionItem>();
|
||||
|
||||
if (!Schema.TryGetValue(elementName, out var schema))
|
||||
return items;
|
||||
|
||||
var sortIndex = 0;
|
||||
foreach (var (attrName, attrSchema) in schema.Attributes)
|
||||
{
|
||||
var insertText = attrSchema.Values.Count > 0
|
||||
? $"{attrName}=\"${{1|{string.Join(",", attrSchema.Values)}|}}\""
|
||||
: $"{attrName}=\"$1\"";
|
||||
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = attrName,
|
||||
Kind = CompletionItemKind.Property,
|
||||
Detail = attrSchema.Description,
|
||||
InsertText = insertText,
|
||||
InsertTextRules = CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
SortText = sortIndex++.ToString("D3"),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<CompletionItem> GetAttributeValueCompletions(string elementName, string attributeName)
|
||||
{
|
||||
var items = new List<CompletionItem>();
|
||||
|
||||
if (!Schema.TryGetValue(elementName, out var schema))
|
||||
return items;
|
||||
|
||||
if (!schema.Attributes.TryGetValue(attributeName, out var attrSchema))
|
||||
return items;
|
||||
|
||||
foreach (var value in attrSchema.Values)
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = value,
|
||||
Kind = CompletionItemKind.Value,
|
||||
InsertText = value,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<CompletionItem> GetExpressionCompletions(string prefix, string[] lines, int lineNumber)
|
||||
{
|
||||
var items = new List<CompletionItem>();
|
||||
var sortIndex = 0;
|
||||
|
||||
// Loop variables
|
||||
var loopVars = FindLoopVariables(lines, lineNumber);
|
||||
|
||||
// Special loop variables
|
||||
foreach (var special in new[] { "$index", "$first", "$last" })
|
||||
{
|
||||
if (loopVars.Count > 0) // Only suggest if inside a foreach
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = special,
|
||||
Kind = CompletionItemKind.Variable,
|
||||
Detail = special switch
|
||||
{
|
||||
"$index" => "Zero-based loop index",
|
||||
"$first" => "True on first iteration",
|
||||
"$last" => "True on last iteration",
|
||||
_ => ""
|
||||
},
|
||||
InsertText = special,
|
||||
SortText = sortIndex++.ToString("D3"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Loop variable property paths from JSON
|
||||
foreach (var loopVar in loopVars)
|
||||
{
|
||||
// Find array in JSON paths and suggest its item properties
|
||||
var arrayPaths = FindMatchingArrayPaths(loopVar.ItemsPath);
|
||||
|
||||
foreach (var prop in arrayPaths)
|
||||
{
|
||||
// prop.Path is like "Items[].Description" - we want "item.Description"
|
||||
var suffix = prop.Path;
|
||||
var bracketIdx = suffix.IndexOf("[].", StringComparison.Ordinal);
|
||||
if (bracketIdx >= 0)
|
||||
suffix = suffix[(bracketIdx + 3)..];
|
||||
else
|
||||
continue;
|
||||
|
||||
var label = $"{loopVar.VarName}.{suffix}";
|
||||
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = label,
|
||||
Kind = CompletionItemKind.Field,
|
||||
Detail = $"{prop.Type} (from {loopVar.ItemsPath})",
|
||||
InsertText = label,
|
||||
SortText = sortIndex++.ToString("D3"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level JSON paths
|
||||
foreach (var prop in _jsonPaths.Where(p => !p.Path.Contains("[].")))
|
||||
{
|
||||
var kind = prop.IsArray ? CompletionItemKind.Enum : CompletionItemKind.Field;
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = prop.Path,
|
||||
Kind = kind,
|
||||
Detail = prop.Type,
|
||||
InsertText = prop.Path,
|
||||
SortText = sortIndex++.ToString("D3"),
|
||||
});
|
||||
}
|
||||
|
||||
// Format specifiers
|
||||
if (prefix.Contains(':'))
|
||||
{
|
||||
items.Clear();
|
||||
foreach (var (fmt, desc) in FormatSpecifiers)
|
||||
{
|
||||
items.Add(new CompletionItem
|
||||
{
|
||||
LabelAsString = fmt,
|
||||
Kind = CompletionItemKind.Unit,
|
||||
Detail = desc,
|
||||
InsertText = fmt,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static readonly (string Format, string Description)[] FormatSpecifiers =
|
||||
[
|
||||
("F2", "Fixed-point 2 decimals (9.50)"),
|
||||
("F0", "Fixed-point no decimals (10)"),
|
||||
("N0", "Number with separators (1,234)"),
|
||||
("N2", "Number with separators + decimals (1,234.56)"),
|
||||
("dd.MM.yyyy", "Date (15.03.2024)"),
|
||||
("HH:mm", "Time (14:30)"),
|
||||
("HH:mm:ss", "Time with seconds (14:30:00)"),
|
||||
("dd-MMM-yy HH:mm", "Date + time (15-Mar-24 14:30)"),
|
||||
("dd.MM.yyyy HH:mm", "Date + time (15.03.2024 14:30)"),
|
||||
];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Loop Variable Detection
|
||||
|
||||
private record LoopVarInfo(string VarName, string ItemsPath);
|
||||
|
||||
private static List<LoopVarInfo> FindLoopVariables(string[] lines, int lineNumber)
|
||||
{
|
||||
var vars = new List<LoopVarInfo>();
|
||||
|
||||
// Walk backwards from current line to find enclosing foreach elements
|
||||
var depth = 0;
|
||||
for (var i = lineNumber - 1; i >= 0; i--)
|
||||
{
|
||||
var line = lines[i].Trim();
|
||||
|
||||
// Count closing foreach tags (increase depth needed)
|
||||
if (line.Contains("</foreach>"))
|
||||
depth++;
|
||||
|
||||
// Check for opening foreach tags
|
||||
var foreachMatch = System.Text.RegularExpressions.Regex.Match(line,
|
||||
@"<foreach\s+items=""([^""]+)""\s+var=""([^""]+)""");
|
||||
if (!foreachMatch.Success)
|
||||
foreachMatch = System.Text.RegularExpressions.Regex.Match(line,
|
||||
@"<foreach\s+var=""([^""]+)""\s+items=""([^""]+)""");
|
||||
|
||||
if (foreachMatch.Success)
|
||||
{
|
||||
if (depth > 0)
|
||||
{
|
||||
depth--;
|
||||
continue;
|
||||
}
|
||||
|
||||
string itemsPath, varName;
|
||||
if (foreachMatch.Groups.Count >= 3)
|
||||
{
|
||||
// Determine which capture is items vs var based on pattern
|
||||
if (line.IndexOf("items=", StringComparison.Ordinal) < line.IndexOf("var=", StringComparison.Ordinal))
|
||||
{
|
||||
itemsPath = foreachMatch.Groups[1].Value;
|
||||
varName = foreachMatch.Groups[2].Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
varName = foreachMatch.Groups[1].Value;
|
||||
itemsPath = foreachMatch.Groups[2].Value;
|
||||
}
|
||||
|
||||
vars.Add(new LoopVarInfo(varName, itemsPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
private List<JsonPropertyInfo> FindMatchingArrayPaths(string itemsPath)
|
||||
{
|
||||
// itemsPath could be "Items" or "gang.Dishes" etc.
|
||||
// We need to find JSON paths that start with the matching array path + "[]."
|
||||
return _jsonPaths
|
||||
.Where(p => p.Path.StartsWith(itemsPath + "[].", StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user