Wave 3 of the Monaco/Roslyn integration. Adds the four extensions
agreed in the design Q&A:
1. Parameters["..."] keys — when the cursor is inside a string
literal that's the index of a Parameters[] element-access,
completions return the parameter names declared in the form's
ParameterListEditor.
2. CallShared("...") names — when the cursor is inside a string
literal argument to a CallShared(...) invocation, completions
return the names of all shared scripts (resolved server-side
via SharedScriptService).
3. CallScript("...") names — same shape, but uses sibling-script
names passed from the form (TemplateEdit's _scripts list).
4. Forbidden-API diagnostic — squiggles uses of the documented
script trust model bans: System.IO / Diagnostics / Reflection /
Net / Threading.Thread namespaces, plus the named types File,
Directory, Process, Thread, Socket, etc. New diagnostic codes
SCADA001 (using directive) and SCADA002 (type identifier).
ScriptAnalysisService gains a SharedScriptService dependency
(scoped, hence the analyzer is now scoped too); CompletionsRequest
carries DeclaredParameters and SiblingScripts; Complete is now async.
MonacoEditor.razor exposes DeclaredParameters / SiblingScripts
parameters plus a [JSInvokable] GetContext() so the JS side asks
for the latest form state on every completion request. The
provider in monaco-init.js looks up the owning editor from the
internal editors map and forwards the context.
ScriptParameterNames helper parses the ParameterListEditor JSON
into a name list — used by SharedScriptForm, ApiMethodForm, and
TemplateEdit's Add-Script form to populate the Monaco context.
Smoke-verified via direct fetch + Monaco trigger:
- var x = Parameters[" → popup: "name" (declared parameter)
- var y = CallShared(" → popup: GetWeather, Greet
- using System.IO; → SCADA001 squiggle
- Process.Start(...) → SCADA002 squiggle
- File.ReadAllText(...) → SCADA002 squiggle
Also fixed: ScriptAnalysisService scoped (was singleton, broke DI
because SharedScriptService is scoped); JS normalizes Pascal-case
context keys from Blazor's record serialization to camel-case for
the request body.
Adds Microsoft.CodeAnalysis.CSharp.Scripting (4.13.0). Scripts are
compiled as C# script fragments against a ScriptHost globals type
that mirrors what the runtime exposes (Parameters bag, CallShared,
CallScript) — Roslyn reads the signatures so those identifiers are
in scope for analysis without executing anything.
ScriptAnalysisService:
- Diagnose(code): Compilation.GetDiagnostics() projected to
Monaco-shaped DiagnosticMarker records (severity 8/4/2/1).
- Complete(code, line, col): dot-member lookup via SemanticModel
when the token at position is part of a MemberAccessExpression;
falls back to LookupSymbols at position for the general case.
Two endpoints exposed by the existing CentralUI endpoint pipeline,
both behind RequireDesign policy:
POST /api/script-analysis/diagnostics
POST /api/script-analysis/completions
monaco-init.js registers a csharp CompletionItemProvider with dot/
paren/quote trigger chars, plus a 500 ms debounced diagnostics pass
on every keystroke that pushes markers via setModelMarkers. Initial
pass fires on editor create so existing scripts surface errors right
away. Auth uses the existing cookie via credentials: same-origin.
Smoke-verified:
- Typing `DateTimeOffset.UtcNow` (no semicolon) shows the missing
semicolon squiggle in real time.
- Ctrl-Space at file scope returns the full type universe
(AccessViolationException, Action, Akka, AppDomain, ...).
Wave 2 of three. SCADA-specific extensions (declared param keys,
shared/sibling script names, forbidden-API diagnostic) follow.