Files
scadalink-design/src/ScadaLink.CentralUI/Components/Shared/ProblemsPanel.razor
Joseph Doherty 0528c65cba feat(ui/scripts): format, inlay hints, problems panel, type diagnostic
Three more editor features rolled in:

1. Roslyn Format command.
   New POST /api/script-analysis/format runs Formatter.Format() from
   Microsoft.CodeAnalysis.CSharp.Workspaces on the parsed script
   tree. monaco-init.js registers a DocumentFormattingEditProvider
   so Ctrl/Cmd-Shift-F and the toolbar "Format" button both work.

2. Inlay hints with parameter names.
   New POST /api/script-analysis/inlay-hints walks CallShared /
   CallScript invocations and emits InlayHint records positioned at
   each argument with the matching parameter's name (e.g. "name:").
   Ghost text appears via Monaco's InlayHintsProvider.

3. SCADA005 argument-type diagnostic.
   Literal type vs. declared parameter type check on every
   CallShared/CallScript argument. Float accepts Integer literals;
   Object/List accept anything; null only matches reference-ish
   types. Legacy lowercase types ("string" etc) from the DB are
   normalized to the canonical set before comparison so existing
   data doesn't false-negative. Non-literal args (variables,
   expressions) are skipped — out of scope for a cheap pass.

4. Parameters["name"] hover.
   Hover endpoint now also resolves Parameters["X"] element-access
   keys against the form's DeclaredParameterShapes and returns
   "parameter `name: String`"-style markdown. MonacoEditor surfaces
   the new DeclaredParameterShapes parameter; ScriptParameterNames
   gets a ParseShapes companion.

5. Problems panel.
   Bootstrap card under the editor listing every marker with
   severity badge, line number, message, and SCADA / CS code. Click
   a row to scroll the editor to that line and focus. JS now
   invokes OnMarkersChanged on the .NET side whenever
   setModelMarkers fires, so the panel stays in sync with the
   editor.

6. Editor toolbar.
   Small top-right strip on each editor with Format / Wrap /
   Minimap / Theme toggles. New MonacoBlazor.format,
   setEditorOption, and revealLine JS APIs back the buttons and the
   problems-panel scroll-to-line.

Contracts:
  - FormatRequest / FormatResponse
  - InlayHintsRequest / InlayHintsResponse / InlayHint
  - HoverRequest.DeclaredParameters
  - MonacoEditor.DeclaredParameterShapes parameter
  - MonacoEditor.MarkersChanged callback
  - ScadaContext.DeclaredParameterShapes

10 new xUnit tests covering format, inlay hints, SCADA005 (string-
expects-integer, integer-expects-string, float-accepts-integer,
object-accepts-anything, non-literal-skipped), and Parameters key
hover. Total: 139 -> 149.

Microsoft.CodeAnalysis.CSharp.Workspaces 4.13.0 added to pull in
Formatter and AdhocWorkspace.

Browser-verified: typing `CallShared("Greet", 42)` now shows the
"name:" inlay hint and a SCADA005 squiggle on `42`; Parameters["typo"]
shows SCADA003 as before; the toolbar buttons all work.
2026-05-12 05:28:13 -04:00

69 lines
2.5 KiB
Plaintext

@namespace ScadaLink.CentralUI.Components.Shared
@using ScadaLink.CentralUI.ScriptAnalysis
@if (Markers.Count > 0)
{
<div class="card mt-2 mb-3">
<div class="card-header py-1 small d-flex justify-content-between align-items-center">
<span>
@if (_errorCount > 0)
{
<span class="badge bg-danger me-1">@_errorCount error@(_errorCount == 1 ? "" : "s")</span>
}
@if (_warningCount > 0)
{
<span class="badge bg-warning text-dark me-1">@_warningCount warning@(_warningCount == 1 ? "" : "s")</span>
}
@if (_infoCount > 0)
{
<span class="badge bg-info text-dark me-1">@_infoCount info</span>
}
</span>
<span class="text-muted">Problems</span>
</div>
<ul class="list-unstyled mb-0 small" style="max-height: 180px; overflow-y: auto;">
@foreach (var m in Markers)
{
<li class="d-flex gap-2 align-items-start px-2 py-1 border-bottom">
<span class="badge @SeverityBadge(m.Severity)" style="min-width: 60px;">@SeverityLabel(m.Severity)</span>
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none flex-grow-1 text-start"
@onclick="@(() => OnNavigate.InvokeAsync(m))">
<span class="text-muted me-2">Line @m.StartLineNumber</span>@m.Message
</button>
<code class="text-muted small">@m.Code</code>
</li>
}
</ul>
</div>
}
@code {
[Parameter, EditorRequired] public IReadOnlyList<DiagnosticMarker> Markers { get; set; } = Array.Empty<DiagnosticMarker>();
[Parameter] public EventCallback<DiagnosticMarker> OnNavigate { get; set; }
private int _errorCount;
private int _warningCount;
private int _infoCount;
protected override void OnParametersSet()
{
_errorCount = Markers.Count(m => m.Severity >= 8);
_warningCount = Markers.Count(m => m.Severity == 4);
_infoCount = Markers.Count(m => m.Severity > 0 && m.Severity < 4);
}
private static string SeverityBadge(int sev) => sev switch
{
>= 8 => "bg-danger",
4 => "bg-warning text-dark",
_ => "bg-info text-dark"
};
private static string SeverityLabel(int sev) => sev switch
{
>= 8 => "Error",
4 => "Warning",
_ => "Info"
};
}