fix(adminui): ctx-receiver guard + truthful SetVirtualTag hover in script-editor completions
This commit is contained in:
@@ -161,7 +161,7 @@ public sealed class ScriptAnalysisService
|
||||
// position is the offset just AFTER the caret; -1 finds the token the caret sits at/just-after (e.g. the dot in "ctx.").
|
||||
var token = root.FindToken(Math.Max(0, position - 1));
|
||||
|
||||
if (_catalog != null && TryGetTagPathLiteral(token, out var pathPrefix))
|
||||
if (_catalog != null && TryGetTagPathLiteral(token, out var pathPrefix, out _))
|
||||
{
|
||||
const string equipDot = EquipmentScriptPaths.EquipToken + "."; // "{{equip}}."
|
||||
if (pathPrefix.StartsWith(equipDot, StringComparison.Ordinal))
|
||||
@@ -210,9 +210,10 @@ public sealed class ScriptAnalysisService
|
||||
.Select(ToCompletionItem).Take(200).ToList();
|
||||
}
|
||||
|
||||
private static bool TryGetTagPathLiteral(SyntaxToken token, out string prefix)
|
||||
private static bool TryGetTagPathLiteral(SyntaxToken token, out string prefix, out bool isSetVirtualTag)
|
||||
{
|
||||
prefix = "";
|
||||
isSetVirtualTag = false;
|
||||
var literal = token.Parent as LiteralExpressionSyntax
|
||||
?? token.GetPreviousToken().Parent as LiteralExpressionSyntax;
|
||||
if (literal is null || !literal.IsKind(SyntaxKind.StringLiteralExpression)) return false;
|
||||
@@ -220,11 +221,16 @@ public sealed class ScriptAnalysisService
|
||||
if (arg.Parent is not ArgumentListSyntax argList) return false;
|
||||
if (argList.Parent is not InvocationExpressionSyntax inv) return false;
|
||||
if (inv.Expression is not MemberAccessExpressionSyntax ma) return false;
|
||||
// Receiver guard: only ctx.GetTag(...) / ctx.SetVirtualTag(...) are real tag-path calls. Mirrors the
|
||||
// runtime harvest (EquipmentScriptPaths.GetTagRefRegex is syntactically `ctx`-anchored), so the editor
|
||||
// offers tag completions/hover for exactly what Phase7Composer harvests — not an unrelated x.GetTag(...).
|
||||
if (ma.Expression is not IdentifierNameSyntax { Identifier.ValueText: "ctx" }) return false;
|
||||
var method = ma.Name.Identifier.ValueText;
|
||||
if (method is not ("GetTag" or "SetVirtualTag")) return false;
|
||||
// only the FIRST argument is the path
|
||||
if (argList.Arguments.Count > 0 && argList.Arguments[0] != arg) return false;
|
||||
prefix = literal.Token.ValueText ?? "";
|
||||
isSetVirtualTag = method == "SetVirtualTag";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -252,7 +258,7 @@ public sealed class ScriptAnalysisService
|
||||
// Tag-path hover takes priority over C# symbol resolution: when the caret sits on a
|
||||
// ctx.GetTag("…")/ctx.SetVirtualTag("…") path literal, show the resolved tag's info
|
||||
// (or note it's not a known configured tag path) instead of the string-literal symbol.
|
||||
if (_catalog is not null && TryGetTagPathLiteral(token, out var tagPath) && !string.IsNullOrEmpty(tagPath))
|
||||
if (_catalog is not null && TryGetTagPathLiteral(token, out var tagPath, out var isSetVirtualTag) && !string.IsNullOrEmpty(tagPath))
|
||||
{
|
||||
static string Code(string s) => s.Replace("`", "\\`");
|
||||
// Equipment-relative literal (contains the {{equip}} token): it cannot resolve to a
|
||||
@@ -270,6 +276,11 @@ public sealed class ScriptAnalysisService
|
||||
? $"**Tag path** `{Code(tagPath)}`\n\n⚠ Not a known configured tag path."
|
||||
: $"**Tag path** `{Code(info.Path)}`\n\n{info.Kind} · Type **{info.DataType}**"
|
||||
+ (info.DriverInstanceId is null ? "" : $" · Driver `{Code(info.DriverInstanceId)}`");
|
||||
// Truthfulness: ctx.SetVirtualTag(...) is a no-op in the live single-tag evaluator
|
||||
// (RoslynVirtualTagEvaluator drops cross-tag writes; the cascade VirtualTagEngine is dormant).
|
||||
if (isSetVirtualTag)
|
||||
tagMd += "\n\n⚠ Cross-tag `SetVirtualTag` writes are currently dropped in single-tag mode " +
|
||||
"(the cascade engine is not wired into the host); this write will not take effect at runtime.";
|
||||
return new HoverResponse(tagMd);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user