Compare commits
19 Commits
e2da662286
...
5c3aa4d211
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c3aa4d211 | |||
| 8aec438cc5 | |||
| 4d12088fa2 | |||
| 72fcde5b44 | |||
| 9444348c0d | |||
| fc7dc3b57d | |||
| 088fc50ef2 | |||
| 071bed5f94 | |||
| 4a2f7e37e5 | |||
| 9104b6c614 | |||
| 521fb61e44 | |||
| d1434933b4 | |||
| 93f5a745a3 | |||
| 6a9b052fc7 | |||
| b54a6ad29f | |||
| 9afb2d230e | |||
| a2dbc5e2da | |||
| 93aa6c2f81 | |||
| 7a03d01613 |
@@ -158,3 +158,16 @@ Address pickers in AdminUI support live browse for OpcUaClient and Galaxy driver
|
||||
The AdminUI's global **UNS** page (`/uns`) is the single surface for managing the unified namespace fleet-wide (Area → Line → Equipment → Tag/VirtualTag), replacing the old per-cluster UNS/Equipment/Tags tabs. See `docs/Uns.md`.
|
||||
|
||||
The `/uns` **TagModal** uses **driver-typed tag-config editors**: it dispatches by the bound driver's `DriverType` to a per-driver editor (Modbus/S7/AbCip/AbLegacy/TwinCAT/Focas) via `TagConfigEditorMap`, with client-side validation via `TagConfigValidator`; unmapped drivers (OpcUaClient/Galaxy/Historian.Wonderware) fall back to the generic raw-`TagConfig`-JSON textarea. Each editor is a thin razor shell over a pure `<Driver>TagConfigModel` (`FromJson`/`ToJson`/`Validate`, preserves unknown keys). To add a driver's editor, copy the Modbus template under `Components/Shared/Uns/TagEditors/` + `Uns/TagEditors/`, reusing the driver's enums + camelCase JSON property names, and register it in `TagConfigEditorMap` + `TagConfigValidator`. See `docs/plans/2026-06-09-driver-typed-tag-editors-design.md`.
|
||||
|
||||
## Scripting / Script Editor
|
||||
|
||||
C# virtual-tag scripts are authored in a **Roslyn-backed Monaco editor** on the
|
||||
ScriptEdit page (`/scripts/{id}`) and inline inside the virtual-tag modal on the
|
||||
`/uns` page. The editor provides completions, live diagnostics, hover, signature
|
||||
help, document formatting, and tag-path completions inside `ctx.GetTag("…")` /
|
||||
`ctx.SetVirtualTag("…")` literals — all backed by the same compiler context the
|
||||
runtime publish gate uses, so what the editor accepts/rejects matches publish
|
||||
exactly. The backend lives in
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/` (six minimal-API
|
||||
endpoints under `/api/script-analysis/*`, gated by the `FleetAdmin` policy).
|
||||
See `docs/ScriptEditor.md` for the full guide.
|
||||
|
||||
@@ -35,6 +35,7 @@ The project was originally called **LmxOpcUa** (a single-driver Galaxy/MXAccess
|
||||
| [IncrementalSync.md](IncrementalSync.md) | Address-space rebuild on redeploy + `sp_ComputeGenerationDiff` |
|
||||
| [HistoricalDataAccess.md](v1/HistoricalDataAccess.md) | `IHistoryProvider` as a per-driver optional capability (v1 archive) |
|
||||
| [VirtualTags.md](VirtualTags.md) | `Core.Scripting` + `Core.VirtualTags` — Roslyn script sandbox, engine, dispatch alongside driver tags |
|
||||
| [ScriptEditor.md](ScriptEditor.md) | Monaco script editor — Roslyn-backed IntelliSense (completions, diagnostics, hover, tag-path) for C# virtual-tag scripts; `AdminUI/ScriptAnalysis/` backend |
|
||||
| [ScriptedAlarms.md](ScriptedAlarms.md) | `Core.ScriptedAlarms` — script-predicate `IAlarmSource` + Part 9 state machine |
|
||||
|
||||
One Core subsystem is shipped without a dedicated top-level doc; see the section in the linked doc:
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
# Monaco Script Editor (Roslyn-backed IntelliSense)
|
||||
|
||||
The script editor gives AdminUI users a first-class C# authoring experience for
|
||||
virtual-tag scripts: completions, diagnostics, hover, signature help, document
|
||||
formatting, and context-aware tag-path completions — all backed by the same
|
||||
Roslyn compiler the runtime publish gate uses.
|
||||
|
||||
See the design doc for the original rationale and brainstorming record:
|
||||
[`docs/plans/2026-06-09-monaco-script-editor-design.md`](plans/2026-06-09-monaco-script-editor-design.md).
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Virtual-tag scripts are C# expression bodies (see [VirtualTags.md](VirtualTags.md))
|
||||
compiled at publish time by `ScriptEvaluator` against a restricted `ScriptSandbox`.
|
||||
Before this feature the AdminUI's script page used a CDN Monaco instance wired
|
||||
over a hidden textarea via an `eval` + `Task.Delay(50)` race — no IntelliSense,
|
||||
fragile in air-gapped environments, and the editor accepted code that publish
|
||||
would reject.
|
||||
|
||||
The replacement:
|
||||
|
||||
- **Vendored Monaco** served locally — no CDN, air-gap safe.
|
||||
- **Reusable `MonacoEditor.razor`** component wired into both the ScriptEdit page
|
||||
(`/scripts/{id}`) and the virtual-tag inline script panel in the UNS TagModal.
|
||||
- **Full IntelliSense** via a Roslyn backend that analyses the user's source inside
|
||||
the *exact* evaluator wrapper `ScriptEvaluator` compiles, so what the editor
|
||||
accepts/rejects matches publish exactly.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────── AdminUI (Blazor Server) ───────────────────────────┐
|
||||
│ │
|
||||
│ Components/Shared/MonacoEditor.razor ◄── Value/ValueChanged, Height, │
|
||||
│ │ ReadOnly, ShowToolbar, theme/Format/wrap/minimap toolbar │
|
||||
│ │ (DotNetObjectReference both ways) │
|
||||
│ ▼ │
|
||||
│ wwwroot/js/monaco-init.js ──registers──► completion / hover / signatureHelp / │
|
||||
│ │ diagnostics / format / inlayHints │
|
||||
│ │ fetch POST /api/script-analysis/* │
|
||||
│ ▼ │
|
||||
│ ScriptAnalysis/ScriptAnalysisEndpoints.cs (minimal-API group, FleetAdmin) │
|
||||
│ ▼ │
|
||||
│ ScriptAnalysis/ScriptAnalysisService.cs ── Roslyn over OtOpcUa's real wrapper │
|
||||
│ ├─ ScriptSandbox.Build(typeof(VirtualTagContext)) (imports + refs) │
|
||||
│ ├─ CompiledScript.Run(ScriptGlobals<VirtualTagContext>) wrapper │
|
||||
│ ├─ ForbiddenTypeAnalyzer (sandbox-escape squiggles) │
|
||||
│ ├─ DependencyExtractor (dynamic-path squiggles) │
|
||||
│ └─ IScriptTagCatalog (tag-path string-literal completions) │
|
||||
│ ▼ │
|
||||
│ wwwroot/lib/monaco/vs/… (vendored Monaco, loaded by monaco-init.js) │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
│ references
|
||||
▼
|
||||
Core.Scripting + Scripting.Abstractions (ScriptSandbox, VirtualTagContext,
|
||||
ScriptGlobals<>, ForbiddenTypeAnalyzer, DependencyExtractor)
|
||||
Configuration (OtOpcUaConfigDbContext → tag / virtual-tag paths for catalog)
|
||||
```
|
||||
|
||||
### MonacoEditor.razor
|
||||
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/MonacoEditor.razor`
|
||||
|
||||
A reusable Blazor Server component. Key parameters:
|
||||
|
||||
| Parameter | Type | Notes |
|
||||
|-----------|------|-------|
|
||||
| `Value` | `string` | Two-way bound script source |
|
||||
| `ValueChanged` | `EventCallback<string>` | Fires on every model change |
|
||||
| `Height` | `string` | CSS height (default `400px`) |
|
||||
| `ReadOnly` | `bool` | Disables editing; IntelliSense still works |
|
||||
| `ShowToolbar` | `bool` | Shows the theme/Format/wrap/minimap toolbar |
|
||||
|
||||
The component creates the Monaco editor in `OnAfterRenderAsync`, registers a
|
||||
`DotNetObjectReference` so JS can call back into Blazor on value change, and
|
||||
uses a GUID-keyed container so multiple instances on the same page coexist safely.
|
||||
External `Value` changes are pushed to the editor via `window.MonacoBlazor.setValue`.
|
||||
|
||||
### monaco-init.js
|
||||
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-init.js`
|
||||
|
||||
Exposes `window.MonacoBlazor` with `createEditor / setValue / getValue /
|
||||
setMarkers / setEditorOption / format / dispose`. Registers the six Monaco
|
||||
language providers for the `csharp` language at load time; each provider POSTs
|
||||
to the corresponding `/api/script-analysis/*` endpoint. Diagnostics are
|
||||
debounced (~500 ms) to avoid firing on every keystroke.
|
||||
|
||||
### ScriptAnalysisService
|
||||
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs`
|
||||
|
||||
The analysis seam is the private `Analyze(userSource)` method, which:
|
||||
|
||||
1. Builds the wrapped document — the user's source is appended after the
|
||||
OtOpcUa evaluator preamble (usings from `ScriptSandbox.Build`, the
|
||||
`CompiledScript.Run(ScriptGlobals<VirtualTagContext>)` wrapper, `var ctx =
|
||||
globals.ctx;`, then `#line 1` so Roslyn reports user-source coordinates for
|
||||
diagnostics). Return type is `object` so one shared script is valid regardless
|
||||
of any virtual tag's `DataType`.
|
||||
2. Parses the wrapped document as a `CSharpSyntaxTree` and creates a
|
||||
`CSharpCompilation` against the `ScriptSandbox` reference set.
|
||||
3. Returns the `SemanticModel`, `SyntaxTree`, and the preamble byte offset (used
|
||||
by every capability to map editor coordinates to wrapped-document offsets).
|
||||
|
||||
---
|
||||
|
||||
## HTTP Endpoints
|
||||
|
||||
All six endpoints live under the `/api/script-analysis` group, registered by
|
||||
`ScriptAnalysisEndpoints.MapScriptAnalysisEndpoints()` (called from
|
||||
`EndpointRouteBuilderExtensions.AddAdminUI` / `Host/Program.cs`) and gated by
|
||||
the `FleetAdmin` authorization policy (requires the `Administrator` role).
|
||||
|
||||
| Route | Request DTO | Response DTO | Purpose |
|
||||
|-------|-------------|--------------|---------|
|
||||
| `POST /api/script-analysis/diagnostics` | `DiagnoseRequest(Code)` | `DiagnoseResponse(Markers)` | Roslyn + ForbiddenType + dynamic-path error markers |
|
||||
| `POST /api/script-analysis/completions` | `CompletionsRequest(CodeText, Line, Column)` | `CompletionsResponse(Items)` | Scope, dot-member, and tag-path completions |
|
||||
| `POST /api/script-analysis/hover` | `HoverRequest(CodeText, Line, Column)` | `HoverResponse(Markdown?)` | Type + XML doc markdown |
|
||||
| `POST /api/script-analysis/signature-help` | `SignatureHelpRequest(CodeText, Line, Column)` | `SignatureHelpResponse(Label?, Parameters?, ActiveParameter)` | Parameter help |
|
||||
| `POST /api/script-analysis/format` | `FormatRequest(Code)` | `FormatResponse(Code)` | `NormalizeWhitespace()` formatting |
|
||||
| `POST /api/script-analysis/inlay-hints` | `InlayHintsRequest(Code)` | `InlayHintsResponse(Hints)` | Inline type hints (stub — returns empty) |
|
||||
|
||||
DTO definitions live in `ScriptAnalysis/ScriptAnalysisContracts.cs`.
|
||||
|
||||
`DiagnosticMarker` fields: `Severity` (Monaco values: 8 = error, 4 = warning,
|
||||
2 = info, 1 = hint), `StartLineNumber`, `StartColumn`, `EndLineNumber`,
|
||||
`EndColumn`, `Message`, `Code`.
|
||||
|
||||
`CompletionItem` fields: `Label`, `InsertText`, `Detail`, `Kind` (Monaco kind
|
||||
string: `"Method"`, `"Property"`, `"Field"`, `"Variable"`, `"Class"`, etc.),
|
||||
`InsertTextRules` (0 for plain text, 4 for snippet).
|
||||
|
||||
---
|
||||
|
||||
## Diagnostics Composition
|
||||
|
||||
The diagnostics endpoint combines three sources and returns **errors only**:
|
||||
|
||||
### 1. Roslyn compile diagnostics
|
||||
|
||||
`compilation.GetDiagnostics()` filtered to `DiagnosticSeverity.Error`. Warnings
|
||||
are intentionally excluded: the canonical passthrough pattern
|
||||
`return ctx.GetTag("X").Value;` triggers nullable warning CS8605 under
|
||||
`NullableContextOptions.Enable` but compiles and publishes fine — flagging it in
|
||||
the editor would be misleading noise that does not reflect publish reality.
|
||||
|
||||
Line numbers are read from `GetMappedLineSpan()` (not `GetLineSpan()`).
|
||||
`GetMappedLineSpan` honours the `#line 1` directive in the preamble, so it
|
||||
returns user-source coordinates directly. Diagnostics whose
|
||||
`SourceSpan.Start < preambleLength` are dropped — they are phantom wrapper
|
||||
diagnostics (e.g. `CS0161` "not all code paths return a value" on the synthesized
|
||||
`Run` method body when the user has not yet typed `return`) and are not the
|
||||
user's fault.
|
||||
|
||||
### 2. ForbiddenTypeAnalyzer
|
||||
|
||||
`ForbiddenTypeAnalyzer.Analyze(compilation)` walks the wrapped syntax tree and
|
||||
resolves every referenced symbol against the sandbox deny-list (namespace
|
||||
prefixes `System.IO`, `System.Net`, `System.Diagnostics`, `System.Reflection`,
|
||||
`System.Threading.Tasks`, etc., plus type-granular denials like
|
||||
`System.Environment` and `System.Threading.Thread`). Each violation is mapped
|
||||
via `tree.GetMappedLineSpan(span)` to user-source coordinates and emitted as an
|
||||
error marker with code `OTSCRIPT_FORBIDDEN`.
|
||||
|
||||
### 3. DependencyExtractor
|
||||
|
||||
`DependencyExtractor.Extract(code).Rejections` enumerates every `ctx.GetTag` /
|
||||
`ctx.SetVirtualTag` call whose first argument is NOT a string literal (variable,
|
||||
concatenation, interpolation). Each rejection carries a `TextSpan` in
|
||||
user-source coordinates, emitted as code `OTSCRIPT_DYNPATH`. This is the exact
|
||||
same check publish enforces — the editor shows the squiggle where the literal
|
||||
must appear.
|
||||
|
||||
---
|
||||
|
||||
## Tag-Path Completion
|
||||
|
||||
When the caret is inside the first string-literal argument of a `GetTag("…")` or
|
||||
`SetVirtualTag("…")` invocation, the completion provider delegates to
|
||||
`IScriptTagCatalog` instead of the semantic model.
|
||||
|
||||
### IScriptTagCatalog contract
|
||||
|
||||
`ScriptAnalysis/IScriptTagCatalog.cs`
|
||||
`GetPathsAsync(string? filter, CancellationToken)` returns the distinct
|
||||
**runtime-resolvable keys** a script may pass to `ctx.GetTag` /
|
||||
`ctx.SetVirtualTag`, optionally filtered by a case-insensitive `StartsWith`
|
||||
prefix. The concrete implementation `ScriptTagCatalog` reads `Tag` +
|
||||
`VirtualTag` rows from `OtOpcUaConfigDbContext` and projects them as follows:
|
||||
|
||||
| Tag category | Resolvable key emitted |
|
||||
|---|---|
|
||||
| Equipment driver tag (`EquipmentId != null`) | Driver `FullName` extracted from `Tag.TagConfig` JSON — the verified `DependencyMux` key |
|
||||
| SystemPlatform / Galaxy tag (`EquipmentId == null`) | MXAccess dot-ref: `FolderPath.Name` when a folder is set, else `Name` |
|
||||
| Virtual tag | Leaf `Name` (best-effort) |
|
||||
|
||||
**Why only resolvable keys?** The live runtime resolves a `ctx.GetTag("X")`
|
||||
literal against `DriverInstanceActor.AttributeValuePublished.FullReference`,
|
||||
which is the `FullName` field from `Tag.TagConfig`
|
||||
(see `Phase7Composer.ExtractTagFullName` + `EquipmentNodeWalker.ExtractFullName`).
|
||||
The UNS-path engine (`Core.VirtualTags.VirtualTagEngine`, keyed by the
|
||||
slash-joined `Enterprise/Site/Area/Line/Equipment/TagName` browse path) is
|
||||
dormant — it is not wired into the host — so UNS browse paths never resolve at
|
||||
runtime and are intentionally NOT offered as completions.
|
||||
|
||||
The catalog is scoped per Blazor circuit; each call creates and disposes its own
|
||||
`DbContext` via the pooled factory (same pattern as `UnsTreeService`). Results
|
||||
are bounded to 200 entries to keep the completion list responsive on large fleets.
|
||||
|
||||
### Literal gate
|
||||
|
||||
`TryGetTagPathLiteral` identifies the tag-path context by climbing from the
|
||||
syntax token under the caret to: `LiteralExpressionSyntax` → `ArgumentSyntax`
|
||||
(first argument only) → `ArgumentListSyntax` → `InvocationExpressionSyntax` →
|
||||
`MemberAccessExpressionSyntax`. The method name check is
|
||||
`method is "GetTag" or "SetVirtualTag"` — it matches by method name only, not
|
||||
by receiver type, so `anything.GetTag("…")` would also trigger tag-path
|
||||
completion. This is harmless in the sandbox (no other `GetTag` methods exist in
|
||||
the allowed reference set) and keeps the detection simple.
|
||||
|
||||
---
|
||||
|
||||
## Vendoring / Air-gap Safety
|
||||
|
||||
Monaco is served from
|
||||
`_content/ZB.MOM.WW.OtOpcUa.AdminUI/lib/monaco/vs/` (the Blazor static-asset
|
||||
path for the AdminUI RCL). The loader script is at
|
||||
`_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-init.js`. No CDN is contacted at
|
||||
runtime. The legacy CDN loader (`monaco-loader.js`) and the `eval`/`Task.Delay`
|
||||
injection previously in `ScriptEdit.razor` have been removed.
|
||||
|
||||
---
|
||||
|
||||
## Wire-in Points
|
||||
|
||||
### ScriptEdit page (`/scripts/{id}`)
|
||||
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor`
|
||||
|
||||
Hosts `<MonacoEditor @bind-Value="_form.SourceCode">`. On save, SHA-256 is
|
||||
recomputed from the editor value and stored in `Script.SourceHash` — unchanged
|
||||
from the pre-Monaco save path.
|
||||
|
||||
### VirtualTagModal (`/uns` TagModal for virtual tags)
|
||||
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/VirtualTagModal.razor`
|
||||
|
||||
When the selected script has a `ScriptId`, an inline "Script source" panel
|
||||
renders `<MonacoEditor>` bound to the script source. The panel shows a
|
||||
**"Used by N virtual tag(s)"** notice (populated from
|
||||
`IUnsTreeService.CountVirtualTagsUsingScriptAsync`). A dedicated **Save script**
|
||||
button calls `IUnsTreeService.UpdateScriptSourceAsync`, which performs a
|
||||
`RowVersion`-guarded update of the `Script` row — a separate concurrency unit
|
||||
from the virtual-tag Create/Save operation. Creating a brand-new script inline
|
||||
from the modal is a follow-up (not v1).
|
||||
|
||||
Three `IUnsTreeService` methods back the inline panel:
|
||||
`GetScriptSourceAsync` / `CountVirtualTagsUsingScriptAsync` /
|
||||
`UpdateScriptSourceAsync`.
|
||||
|
||||
---
|
||||
|
||||
## How to Extend
|
||||
|
||||
### Adding a new completion source
|
||||
|
||||
1. Implement a new case in `ScriptAnalysisService.CompleteAsync`. The
|
||||
`Analyze(code)` seam gives you the `SemanticModel`, `SyntaxTree`, and
|
||||
preamble offset.
|
||||
2. Add the corresponding request/response DTOs to
|
||||
`ScriptAnalysisContracts.cs` if a new endpoint is needed, or fold into an
|
||||
existing endpoint with an extra field.
|
||||
3. Register the new Monaco provider in `monaco-init.js` using the same
|
||||
`fetch('/api/script-analysis/…', { method: 'POST', … })` pattern.
|
||||
|
||||
### Adding a new analysis check (diagnostics)
|
||||
|
||||
Extend `Diagnose` in `ScriptAnalysisService`. The existing three-source pattern
|
||||
(Roslyn → `ForbiddenTypeAnalyzer` → `DependencyExtractor`) is additive: add a
|
||||
fourth source and append to `markers`. Keep severity at 8 (Monaco error) for
|
||||
anything that blocks publish; use 4 (warning) for advisory-only hints.
|
||||
|
||||
### Changing the tag-path catalog source
|
||||
|
||||
Replace or extend the `ScriptTagCatalog` implementation of `IScriptTagCatalog`.
|
||||
Register the replacement in `EndpointRouteBuilderExtensions.AddAdminUI`
|
||||
(or inject via the DI container as the `IScriptTagCatalog` scoped service).
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations / Follow-ups
|
||||
|
||||
- **Tag-path completion shows resolvable keys only.** Surfacing the UNS browse
|
||||
path as a completion *detail* (a non-inserted hint shown alongside the
|
||||
resolvable key) for discoverability is a tracked follow-up.
|
||||
- **Literal gate matches by method name, not receiver.** `anything.GetTag("…")`
|
||||
would also trigger tag-path completion (not just `ctx.GetTag("…")`). Harmless
|
||||
in the sandbox today — no other `GetTag` methods exist in the allowed reference
|
||||
set — but tighter binding to `ctx` / `VirtualTagContext` is a follow-up.
|
||||
- **Analysis runs synchronous Roslyn (CPU-bound).** Each endpoint call builds a
|
||||
`CSharpCompilation` synchronously. This is intentional: the endpoints are
|
||||
admin-only, client-side debounced (~500 ms), and `Task.Run` offload adds
|
||||
unnecessary complexity for infrequent admin use. If concurrency becomes a
|
||||
concern under heavy admin usage, wrapping each `Analyze` call in `Task.Run` is
|
||||
the correct fix.
|
||||
- **InlayHints is a stub.** `/api/script-analysis/inlay-hints` returns an empty
|
||||
list. Filling it in (parameter name hints from the semantic model) is a
|
||||
follow-up task.
|
||||
- **Language providers register globally at Monaco load.** The six providers are
|
||||
registered for the `csharp` language on the global Monaco instance (loaded once
|
||||
per page in `App.razor`). There is no impact today because only
|
||||
`Administrator`-reachable pages host a `MonacoEditor`, but if a read-only
|
||||
surface ever embeds the editor the providers will fire (harmlessly — they just
|
||||
get 401 responses from the `FleetAdmin`-gated endpoints and return empty
|
||||
results).
|
||||
- **Creating a new script from the VirtualTagModal inline panel** is deferred.
|
||||
The current inline panel loads and saves an existing `Script` row; creating a
|
||||
brand-new script requires a separate create flow and is a follow-up.
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
Unit tests live in
|
||||
`tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/`:
|
||||
|
||||
| File | Covers |
|
||||
|------|--------|
|
||||
| `DiagnoseTests.cs` | Clean script (no markers), Roslyn error, ForbiddenTypeAnalyzer violation, dynamic-path rejection, `#line` mapping |
|
||||
| `CompletionTests.cs` | Scope completions, dot-member completions after `ctx.` |
|
||||
| `TagPathCompletionTests.cs` | Tag-path literal completions inside `GetTag`/`SetVirtualTag` via a fake `IScriptTagCatalog` |
|
||||
| `HoverSignatureTests.cs` | Hover markdown, signature-help parameter tracking |
|
||||
| `FormatTests.cs` | `NormalizeWhitespace` round-trip |
|
||||
| `ScriptTagCatalogTests.cs` | Equipment-tag `FullName`, Galaxy dot-ref, virtual-tag leaf `Name`, filter prefix, MaxResults bound |
|
||||
|
||||
`tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/ScriptSourceServiceTests.cs`
|
||||
covers `GetScriptSourceAsync` / `CountVirtualTagsUsingScriptAsync` /
|
||||
`UpdateScriptSourceAsync` via the in-memory EF pattern.
|
||||
|
||||
No bUnit tests exist for `MonacoEditor.razor` or `VirtualTagModal.razor` — the
|
||||
AdminUI has no bUnit dependency. Razor/JS correctness was proven by live
|
||||
docker-dev `/run` (ScriptEdit + UNS TagModal with Monaco, diagnostics, hover,
|
||||
tag-path completions, Format, inline script save).
|
||||
@@ -0,0 +1,285 @@
|
||||
# Monaco Script Editor (Roslyn-backed IntelliSense) — Design
|
||||
|
||||
**Date:** 2026-06-09
|
||||
**Status:** Approved (brainstorming complete; ready for implementation plan)
|
||||
**Author:** brainstorming session
|
||||
**Related:** `docs/Uns.md`, `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/`, sister project `scadabridge` (`/Users/dohertj2/Desktop/scadabridge/src`)
|
||||
|
||||
## Goal
|
||||
|
||||
Replace OtOpcUa's proof-of-concept script editor (a CDN Monaco instance mounted
|
||||
over a hidden textarea via an `eval` + `Task.Delay(50)` race) with a proper,
|
||||
reusable Monaco code editor offering **full Roslyn-backed IntelliSense** — the
|
||||
same class of experience as the sister project **scadabridge**: completions,
|
||||
hover, signature help, live diagnostics, document formatting, inlay hints, and
|
||||
context-aware tag-path completion — but re-seated on OtOpcUa's *actual* script
|
||||
compile context so what the editor accepts/rejects exactly matches what publish
|
||||
enforces.
|
||||
|
||||
## Background — current state
|
||||
|
||||
| Aspect | scadabridge (reference) | OtOpcUa (today) |
|
||||
|---|---|---|
|
||||
| Monaco delivery | Vendored locally (`wwwroot/lib/monaco/vs/`, served from `_content/`), v0.42 | CDN (jsDelivr `monaco-editor@0.52.2`), injected at runtime |
|
||||
| Editor component | Reusable `MonacoEditor.razor` (`Value`/`ValueChanged`, theme, toolbar, `DotNetObjectReference`) | None — `eval`-injected loader over a hidden `<InputTextArea>`, sync-back via dispatched `input` events |
|
||||
| IntelliSense | Full: completions/hover/signature-help/diagnostics/format/inlay via `/api/script-analysis/*` → `ScriptAnalysisService` (Roslyn) | None — plaintext `csharp` highlighting only |
|
||||
| Load robustness | Deterministic | Fragile 50 ms delay; silent fallback to textarea |
|
||||
| Air-gap | Safe (vendored) | Falls back to a plain textarea when CDN unreachable |
|
||||
|
||||
The current code self-documents the gap: comments say richer editing "lands in
|
||||
Phase D.2."
|
||||
|
||||
### OtOpcUa scripting runtime (the context IntelliSense must reproduce)
|
||||
|
||||
- **Evaluator:** `ScriptEvaluator` (`src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptEvaluator.cs`)
|
||||
builds a hand-rolled `CSharpCompilation` (NOT `CSharpScript`). It wraps the
|
||||
user's source in:
|
||||
```csharp
|
||||
using System;
|
||||
using System.Linq;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Compiled;
|
||||
public static class CompiledScript
|
||||
{
|
||||
public static TResult Run(ScriptGlobals<TContext> globals)
|
||||
{
|
||||
var ctx = globals.ctx;
|
||||
#line 1
|
||||
«user source» // statement body, MUST end with an explicit `return`
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Globals / context:** `ScriptGlobals<VirtualTagContext>` exposes `ctx` of type
|
||||
`VirtualTagContext : ScriptContext`. The in-scope API a script author can call:
|
||||
- `ctx.GetTag(string path)` → `DataValueSnapshot` (`.Value`, `.StatusCode`, `.SourceTimestampUtc`, `.ServerTimestampUtc`)
|
||||
- `ctx.SetVirtualTag(string path, object? value)`
|
||||
- `ctx.Now` → `DateTime`
|
||||
- `ctx.Logger` → `Serilog.ILogger`
|
||||
- `ScriptContext.Deadband(double current, double previous, double tolerance)` (static)
|
||||
|
||||
**These are real, referenceable types** (`Scripting.Abstractions`), so Roslyn
|
||||
analysis can point at the genuine types — no mirror "host" classes needed
|
||||
(scadabridge had to build mirrors because its runtime globals weren't
|
||||
directly referenceable).
|
||||
- **Sandbox:** `ScriptSandbox.Build(typeof(VirtualTagContext))`
|
||||
(`src/Core/Scripting/ScriptSandbox.cs`) supplies the imports (above) and the
|
||||
restricted metadata-reference set (pinned OtOpcUa assemblies + a BCL subset;
|
||||
no `System.IO`/`System.Net`/`Process`/`Reflection`).
|
||||
- **Hard gates publish enforces:**
|
||||
- `ForbiddenTypeAnalyzer` — semantic gate against type-forwarding sandbox
|
||||
escapes (e.g. resolving `HttpClient`).
|
||||
- `DependencyExtractor.Extract(source)` — AST walk requiring `ctx.GetTag` /
|
||||
`ctx.SetVirtualTag` paths to be **string literals**; rejects variables,
|
||||
concatenation, interpolation. Also yields the read/write dependency sets.
|
||||
- **Binding:** `VirtualTag.ScriptId` → `Script.ScriptId` (logical FK). A `Script`
|
||||
is **global** — one script may be referenced by multiple virtual tags across
|
||||
different equipment, and those tags may have different `DataType`s. The script
|
||||
is edited as a `Script` row (`SourceCode` + SHA-256 `SourceHash`), not per tag.
|
||||
|
||||
## Decisions (from brainstorming)
|
||||
|
||||
1. **Scope:** Full IntelliSense parity with scadabridge (completions, hover,
|
||||
signature help, diagnostics, formatting, inlay hints) — Roslyn-backed.
|
||||
2. **Location:** Reusable `MonacoEditor.razor`, wired into **both** the
|
||||
ScriptEdit page (`/scripts/{id}`) **and** the Edit-Virtual-Tag modal
|
||||
(inline script-source panel).
|
||||
3. **Tag-path completion:** Yes, in v1 — global completion of all configured
|
||||
tag + virtual-tag paths inside `ctx.GetTag("…")` / `ctx.SetVirtualTag("…")`
|
||||
string literals.
|
||||
4. **Vendoring:** Monaco vendored locally (air-gap safe), replacing the CDN.
|
||||
5. **Modal save-model:** Inline script editing in the modal is its **own**
|
||||
`RowVersion`-guarded Save, separate from the virtual-tag Create/Save, with a
|
||||
"Used by N virtual tags — edits affect all of them" warning.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────────────────── AdminUI (Blazor Server) ────────────────────────────┐
|
||||
│ │
|
||||
│ Components/Shared/MonacoEditor.razor ◄── Value/ValueChanged, theme, toolbar │
|
||||
│ │ (DotNetObjectReference both ways) │
|
||||
│ ▼ │
|
||||
│ wwwroot/js/monaco-init.js ──registers──► completion / hover / signatureHelp / │
|
||||
│ │ diagnostics / format / inlayHints │
|
||||
│ │ fetch POST /api/script-analysis/* │
|
||||
│ ▼ │
|
||||
│ ScriptAnalysis/ScriptAnalysisEndpoints.cs (minimal-API group, authz-gated) │
|
||||
│ ▼ │
|
||||
│ ScriptAnalysis/ScriptAnalysisService.cs ── Roslyn over OtOpcUa's REAL wrapper │
|
||||
│ │ ├─ ScriptSandbox.Build(typeof(VirtualTagContext)) (imports + refs) │
|
||||
│ │ ├─ CompiledScript.Run(ScriptGlobals<VirtualTagContext>) wrapper │
|
||||
│ │ ├─ ForbiddenTypeAnalyzer (sandbox-escape squiggles) │
|
||||
│ │ ├─ DependencyExtractor (dynamic-path squiggles) │
|
||||
│ │ └─ IScriptTagCatalog (tag-path string-literal completions) │
|
||||
│ ▼ │
|
||||
│ wwwroot/lib/monaco/vs/… (vendored Monaco, one <script> in App.razor) │
|
||||
└──────────────────────────────────────────────────────────────────────────────────┘
|
||||
│ references
|
||||
▼
|
||||
Core.Scripting + Scripting.Abstractions (ScriptSandbox, VirtualTagContext,
|
||||
ScriptGlobals<>, ForbiddenTypeAnalyzer, DependencyExtractor)
|
||||
Configuration (OtOpcUaConfigDbContext → tag/virtual-tag paths for the catalog)
|
||||
```
|
||||
|
||||
### Component placement
|
||||
|
||||
`ScriptAnalysis/` lives **inside `ZB.MOM.WW.OtOpcUa.AdminUI`** (mirrors
|
||||
scadabridge's CentralUI layout). New build dependencies on the AdminUI project:
|
||||
|
||||
- Project refs: `Core.Scripting`, `Scripting.Abstractions` (and transitively
|
||||
`Core.Abstractions`). The AdminUI already references `Configuration`.
|
||||
- Packages: `Microsoft.CodeAnalysis.CSharp.Scripting` and
|
||||
`Microsoft.CodeAnalysis.CSharp.Workspaces` (pin to scadabridge's 5.0.0 on
|
||||
net10; confirm at plan time).
|
||||
|
||||
### `ScriptAnalysisService` — the analysis core
|
||||
|
||||
Ported from scadabridge (~90% reusable: position mapping, completion/hover/
|
||||
signature-help/format/inlay extraction, semantic-model access) but re-seated on
|
||||
OtOpcUa's real compile context:
|
||||
|
||||
1. **Wrapped document.** Build the analysis source = OtOpcUa's evaluator wrapper
|
||||
with the user's text spliced after `#line 1`, return type `object` (a shared
|
||||
script must stay valid no matter which virtual tag's `DataType` consumes it;
|
||||
per-tag return coercion remains a runtime concern). Use
|
||||
`ScriptSandbox.Build(typeof(VirtualTagContext))` for imports + references —
|
||||
single source of truth shared with the evaluator.
|
||||
2. **Compilation + semantic model.** `CSharpCompilation.Create` over the wrapped
|
||||
`SyntaxTree`; `compilation.GetSemanticModel(tree)`.
|
||||
3. **Cursor mapping.** Editor `(line, column)` → offset within the user source →
|
||||
add the wrapper-prefix length → physical offset in the wrapped document. The
|
||||
`#line 1` directive makes Roslyn report **diagnostic** line numbers already in
|
||||
user-source coordinates; completions/hover/signature use the physical offset.
|
||||
4. **Completions.** `semanticModel.LookupSymbols(position)` for scope; member
|
||||
completion after `.`; **string-literal completion** inside `ctx.GetTag("…")` /
|
||||
`ctx.SetVirtualTag("…")` delegates to `IScriptTagCatalog` (replaces
|
||||
scadabridge's `Parameters["…"]`/`Attributes["…"]`/`Children["…"]` cases).
|
||||
5. **Diagnostics** (debounced ~500 ms client-side) = union of:
|
||||
- Roslyn compile diagnostics (errors + warnings) on the wrapped compilation.
|
||||
- `ForbiddenTypeAnalyzer` violations → error markers.
|
||||
- `DependencyExtractor` dynamic-path rejections → error markers with the
|
||||
offending span. (These three are precisely what publish enforces.)
|
||||
6. **Hover / signature help / format / inlay hints.** Ported near-verbatim from
|
||||
scadabridge's Roslyn logic; the scadabridge domain-specific string-literal
|
||||
branches are replaced/removed.
|
||||
|
||||
Registered **scoped** (the catalog is scoped over the DB context); metadata
|
||||
references from `ScriptSandbox` are static and cached. A small memory cache
|
||||
(per scadabridge) backs repeated analysis of identical source.
|
||||
|
||||
### `IScriptTagCatalog`
|
||||
|
||||
New scoped service. `GetPathsAsync(string? filter, CancellationToken)` → distinct
|
||||
configured tag + virtual-tag paths from `OtOpcUaConfigDbContext`, used for
|
||||
string-literal completion. Global (scripts are not equipment-scoped). The
|
||||
endpoint passes the current literal prefix as `filter` to bound result size.
|
||||
|
||||
### HTTP endpoints
|
||||
|
||||
`ScriptAnalysis/ScriptAnalysisEndpoints.cs` — `MapScriptAnalysisEndpoints()`
|
||||
maps a `/api/script-analysis` group, gated by the AdminUI's design/write
|
||||
authorization policy:
|
||||
|
||||
| Route | Request | Purpose |
|
||||
|---|---|---|
|
||||
| `POST /diagnostics` | `DiagnoseRequest` | Roslyn + ForbiddenType + dynamic-path markers |
|
||||
| `POST /completions` | `CompletionsRequest` | scope/member + tag-path completion |
|
||||
| `POST /hover` | `HoverRequest` | type/quick-info markdown |
|
||||
| `POST /signature-help` | `SignatureHelpRequest` | parameter help |
|
||||
| `POST /format` | `FormatRequest` | `NormalizeWhitespace()` formatting |
|
||||
| `POST /inlay-hints` | `InlayHintsRequest` | inline type hints |
|
||||
|
||||
DTOs ported from scadabridge's `ScriptAnalysisContracts.cs` minus the
|
||||
`ScriptKind` enum (OtOpcUa has a single script kind: virtual-tag).
|
||||
|
||||
### Front-end
|
||||
|
||||
- **`Components/Shared/MonacoEditor.razor`** — reusable: `[Parameter] Value`,
|
||||
`ValueChanged`, `Height`, `ReadOnly`, `ShowToolbar`, theme toggle (`vs`/
|
||||
`vs-dark`), Format button. Creates the editor in `OnAfterRenderAsync`, two-way
|
||||
binding via `DotNetObjectReference` (`OnValueChanged` → `ValueChanged`),
|
||||
external `Value` changes pushed via `setValue`, `dispose` on teardown,
|
||||
GUID-keyed for multi-instance safety.
|
||||
- **`wwwroot/js/monaco-init.js`** — ported loader + `window.MonacoBlazor` with
|
||||
`createEditor/setValue/getValue/setMarkers/setEditorOption/format/dispose`,
|
||||
and the six Monaco language providers POSTing to `/api/script-analysis/*`
|
||||
(diagnostics debounced).
|
||||
- **Vendored Monaco** under `wwwroot/lib/monaco/vs/…`, pulled in by a single
|
||||
`<script src="_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-init.js">` in
|
||||
`App.razor`. The CDN `monaco-loader.js` + the `eval`/`Task.Delay` injection in
|
||||
`ScriptEdit.razor` are removed.
|
||||
|
||||
### Wire-in points
|
||||
|
||||
- **ScriptEdit page (`/scripts/{id}`):** replace `<InputTextArea>` with
|
||||
`<MonacoEditor @bind-Value="_form.SourceCode" Height="…">`. Save path unchanged
|
||||
(SHA-256 on save). The old `OnAfterRenderAsync` JS-injection block is deleted.
|
||||
- **VirtualTagModal:** below the Script dropdown, a collapsible **"Script source"**
|
||||
panel that loads the selected script's `SourceCode` into a `MonacoEditor`,
|
||||
shows a **"Used by N virtual tags — edits affect all of them"** notice (count
|
||||
from the config DB), and a dedicated **Save script** button performing a
|
||||
`RowVersion`-guarded update of the `Script` row — a separate concurrency unit
|
||||
from the virtual-tag Create/Save. Creating a brand-new script inline is a
|
||||
follow-up, not v1.
|
||||
|
||||
## Data flow (a keystroke)
|
||||
|
||||
1. User types in Monaco → `onDidChangeModelContent` → `ValueChanged` updates the
|
||||
Blazor model; debounce schedules a diagnostics fetch.
|
||||
2. Provider POSTs `{ source, line, column }` to `/api/script-analysis/*`.
|
||||
3. `ScriptAnalysisService` wraps the source, compiles, computes the result at the
|
||||
mapped offset (consulting `IScriptTagCatalog` for path literals), returns DTOs.
|
||||
4. JS provider maps DTOs to Monaco completions/markers/hover and renders them.
|
||||
|
||||
## Error handling
|
||||
|
||||
- **CDN/JS load failure:** with vendoring there is no network dependency; if the
|
||||
vendored asset is somehow missing, the editor surfaces a clear init error
|
||||
rather than silently degrading. (No more silent textarea fallback masking a
|
||||
broken editor.)
|
||||
- **Analysis exceptions:** each endpoint is defensive — a malformed request or an
|
||||
internal Roslyn failure returns an empty result (no completions/markers) rather
|
||||
than 500-ing the editor; logged server-side.
|
||||
- **Concurrency:** modal script Save uses `RowVersion`; a conflict surfaces the
|
||||
standard "changed by someone else, reload" message (existing pattern).
|
||||
- **Auth:** endpoints require the AdminUI design/write policy; read-only users
|
||||
get the editor read-only.
|
||||
|
||||
## Testing
|
||||
|
||||
No bUnit (established AdminUI pattern — razor/JS proven only by live `/run`).
|
||||
|
||||
- **`ScriptAnalysisService` (unit, xUnit + Shouldly):** member completion after
|
||||
`ctx.`; scope completion; tag-path completion inside `GetTag`/`SetVirtualTag`
|
||||
(via a fake `IScriptTagCatalog`); hover on `ctx.GetTag`; signature help; format
|
||||
round-trip; **diagnostics overlays** — a clean script (no markers), a Roslyn
|
||||
error, a `ForbiddenTypeAnalyzer` violation (e.g. `HttpClient`), and a
|
||||
dynamic-path rejection (`ctx.GetTag(someVar)`); cursor-offset/`#line` mapping
|
||||
(marker lands on the right user-source line).
|
||||
- **`IScriptTagCatalog` (unit, in-memory EF — Configuration.Tests pattern):**
|
||||
returns distinct tag + virtual-tag paths; honors the filter prefix.
|
||||
- **Live docker-dev `/run`:** on `/scripts/{id}` — `ctx.` shows members; bad code
|
||||
shows a red squiggle; `ctx.GetTag("` lists tag paths; Format works; theme
|
||||
toggle. Then the VirtualTagModal — open it, pick a script, edit the inline
|
||||
panel, see the "used by N" notice, save, reopen to confirm persistence.
|
||||
|
||||
Done = build clean + `dotnet test` green + live `/run` pass.
|
||||
|
||||
## Out of scope (follow-ups)
|
||||
|
||||
- Creating a brand-new `Script` inline from the VirtualTagModal.
|
||||
- Per-virtual-tag return-type-aware diagnostics (analysis uses `object` return).
|
||||
- Embedding the editor in any surface beyond ScriptEdit + VirtualTagModal.
|
||||
- Sharing the analysis backend as a cross-repo package with scadabridge (this is
|
||||
a copy-and-adapt fork; the context models differ).
|
||||
|
||||
## Hard rules (carried from prior AdminUI work)
|
||||
|
||||
- Stage by path — never `git add .`; never stage `sql_login.txt` or
|
||||
`src/Server/ZB.MOM.WW.OtOpcUa.Host/pki/`; never echo the gateway API key into a
|
||||
new tracked file; no force-push; no `--no-verify`.
|
||||
- The agent does **not** sign in to the AdminUI — the user signs in for live
|
||||
verification.
|
||||
- No Configuration entity/migration change is required by this feature (the
|
||||
catalog reads existing tables; `Script`/`VirtualTag` schemas are unchanged).
|
||||
@@ -0,0 +1,797 @@
|
||||
# Monaco Script Editor (Roslyn-backed IntelliSense) Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans (or subagent-driven-development) to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Replace OtOpcUa's CDN-Monaco-over-textarea script editor with a reusable, Roslyn-backed Monaco editor offering full IntelliSense (completions, hover, signature help, live diagnostics, formatting, tag-path completion), re-seated on OtOpcUa's *real* script compile context so the editor accepts/rejects exactly what publish enforces.
|
||||
|
||||
**Architecture:** A new `ScriptAnalysis/` backend inside the AdminUI exposes `/api/script-analysis/*` minimal-API endpoints backed by `ScriptAnalysisService`, which compiles the user's source inside OtOpcUa's genuine evaluator wrapper (`public static object Run(ScriptGlobals<VirtualTagContext> globals){ var ctx = globals.ctx; #line 1 «source» }`) using `ScriptSandbox.Build(typeof(VirtualTagContext))` for imports + references, then computes Roslyn completions/hover/diagnostics from the semantic model and layers OtOpcUa's `ForbiddenTypeAnalyzer` + `DependencyExtractor` as extra diagnostics. A vendored Monaco + a reusable `MonacoEditor.razor` + ported `monaco-init.js` register language providers that POST to those endpoints. The editor is wired into the ScriptEdit page and the virtual-tag modal.
|
||||
|
||||
**Tech Stack:** .NET 10, Blazor Server (`InteractiveServer`), Roslyn (`Microsoft.CodeAnalysis.CSharp`, transitive via `Core.Scripting`), Monaco Editor (vendored, v0.42 from scadabridge), xUnit + Shouldly, EF Core InMemory (tests).
|
||||
|
||||
**Reference sources to port FROM (sister repo `scadabridge`):**
|
||||
- `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/MonacoEditor.razor`
|
||||
- `src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/js/monaco-init.js`
|
||||
- `src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/lib/monaco/` (vendored Monaco, 15 MB / 121 files)
|
||||
- `src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/{ScriptAnalysisContracts,ScriptAnalysisEndpoints,ScriptAnalysisService}.cs`
|
||||
|
||||
**OtOpcUa types to reuse (do NOT modify):**
|
||||
- `ScriptSandbox.Build(Type) → SandboxConfig(IReadOnlyList<MetadataReference> References, IReadOnlyList<string> Imports)` — `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs:46`
|
||||
- `ForbiddenTypeAnalyzer.Analyze(Compilation) → IReadOnlyList<ForbiddenTypeRejection(TextSpan Span, string TypeName, string Namespace, string Message)>` — `ForbiddenTypeAnalyzer.cs:175,309`
|
||||
- `DependencyExtractor.Extract(string) → DependencyExtractionResult(IReadOnlySet<string> Reads, IReadOnlySet<string> Writes, IReadOnlyList<DependencyRejection(TextSpan Span, string Message)> Rejections){ bool IsValid }` — `DependencyExtractor.cs:42,147,156`
|
||||
- `VirtualTagContext : ScriptContext` (`GetTag(string)→DataValueSnapshot`, `SetVirtualTag(string,object?)`, `Now`, `Logger`, static `ScriptContext.Deadband(double,double,double)`) and `ScriptGlobals<TContext>{ TContext ctx }` — `src/Core/Scripting.Abstractions/`
|
||||
- `DataValueSnapshot(object? Value, uint StatusCode, DateTime? SourceTimestampUtc, DateTime ServerTimestampUtc)` — `Core.Abstractions/DataValueSnapshot.cs:17`
|
||||
- `OtOpcUaConfigDbContext` with `DbSet<Tag> Tags`, `DbSet<VirtualTag> VirtualTags`, `DbSet<Equipment> Equipment`, `DbSet<Script> Scripts` — `Configuration/OtOpcUaConfigDbContext.cs`
|
||||
|
||||
**Existing OtOpcUa files to change:** `Components/Pages/ScriptEdit.razor`, `Components/Shared/Uns/VirtualTagModal.razor`, `Uns/IUnsTreeService.cs` + `UnsTreeService.cs`, `Components/App.razor`, the AdminUI `.csproj`; **delete** `wwwroot/js/monaco-loader.js`. Endpoints mapped in `src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs` (the `hasAdmin` block, after `app.MapAdminUI<App>()`).
|
||||
|
||||
**Hard rules (carry through every task):** AdminUI + new `ScriptAnalysis/` + AdminUI.Tests + the one `Program.cs` map-line only. NO Configuration entity/migration change. Stage by path — never `git add .`; never stage `sql_login.txt` or `src/Server/ZB.MOM.WW.OtOpcUa.Host/pki/`; never echo the gateway API key into a new tracked file; no force-push; no `--no-verify`. The agent does NOT sign in to the AdminUI — the user signs in for the live `/run`.
|
||||
|
||||
**Branch:** Create `feat/monaco-script-editor` off `master` (HEAD = the commit after `7a03d01`/this plan) before any code task. The design doc (`7a03d01`) and this plan + `.tasks.json` are committed to `master` first; the build runs on the feature branch.
|
||||
|
||||
**Authorization:** New endpoints use `.RequireAuthorization("FleetAdmin")` (policy defined in `src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs:113` as `RequireRole("Administrator")` — script design is an admin action, same tier as deploy).
|
||||
|
||||
**Key fidelity notes (read once):**
|
||||
- The analysis return type is `object` (a `Script` is shared across virtual tags of differing `DataType`; per-tag return coercion stays a runtime concern).
|
||||
- The wrapper preamble (usings + `Run` signature + `var ctx = globals.ctx;`) precedes a `#line 1` directive, then the raw user source (column-aligned, not re-indented). Therefore: **diagnostics** — `tree.GetLineSpan(span)` / `d.Location.GetLineSpan()` return *mapped* (user-source) line numbers automatically; **completions/hover/signature** — use the *physical* offset `prefixLength + offsetWithinUserSource`.
|
||||
- Filter out diagnostics physically located in the preamble (`location.SourceSpan.Start < prefixLength`) so wrapper-level noise (e.g. CS0161 "not all code paths return a value" on the `Run` method when the user hasn't typed `return` yet) doesn't show as a phantom marker. (Missing-`return` enforcement stays a runtime/publish concern for v1.)
|
||||
- We use `CSharpCompilation` (not `CSharpScript`), so `Microsoft.CodeAnalysis.CSharp.Scripting` is **not** needed; Roslyn core (`Microsoft.CodeAnalysis.CSharp`, incl. `NormalizeWhitespace`) arrives transitively through the `Core.Scripting` project reference. Add `Microsoft.CodeAnalysis.CSharp.Workspaces` only if a build error names a missing type.
|
||||
|
||||
---
|
||||
|
||||
### Task 0: AdminUI project references + feature branch
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~3 min
|
||||
**Parallelizable with:** none (prerequisite for backend tasks)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ZB.MOM.WW.OtOpcUa.AdminUI.csproj` (ItemGroup of ProjectReferences, ~lines 19–40)
|
||||
|
||||
**Step 1: Create the branch**
|
||||
```bash
|
||||
git checkout -b feat/monaco-script-editor
|
||||
```
|
||||
|
||||
**Step 2: Add the two project references** (alongside the existing `Configuration` ref):
|
||||
```xml
|
||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Scripting\ZB.MOM.WW.OtOpcUa.Core.Scripting.csproj" />
|
||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.csproj" />
|
||||
```
|
||||
(Confirm the exact directory names of those two projects on disk first — `ls src/Core/ | grep Scripting` — and match them.)
|
||||
|
||||
**Step 3: Build to confirm Roslyn resolves transitively**
|
||||
```bash
|
||||
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ZB.MOM.WW.OtOpcUa.AdminUI.csproj
|
||||
```
|
||||
Expected: build succeeds. (`ScriptSandbox`, `CSharpCompilation`, `VirtualTagContext` are now referenceable.)
|
||||
|
||||
**Step 4: Commit**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ZB.MOM.WW.OtOpcUa.AdminUI.csproj
|
||||
git commit -m "build(adminui): reference Core.Scripting for the script-analysis backend"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Vendor Monaco + reusable MonacoEditor.razor + minimal monaco-init.js + App.razor wiring
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 2 (disjoint files)
|
||||
|
||||
**Files:**
|
||||
- Create (copy): `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/lib/monaco/**` (from scadabridge)
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-init.js`
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/MonacoEditor.razor`
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor:23-25` (add one `<script>`)
|
||||
|
||||
No unit tests (UI/JS — proven by the live `/run` in Task 12).
|
||||
|
||||
**Step 1: Vendor Monaco** (file copy, then stage the folder by path):
|
||||
```bash
|
||||
cp -R /Users/dohertj2/Desktop/scadabridge/src/ZB.MOM.WW.ScadaBridge.CentralUI/wwwroot/lib/monaco \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/lib/monaco
|
||||
```
|
||||
(~15 MB / 121 files under `wwwroot/lib/monaco/vs/`. This is the air-gap-safe replacement for the CDN.)
|
||||
|
||||
**Step 2: Create `monaco-init.js`** — port scadabridge's file but **MINIMAL for now** (no language providers yet; those land in Task 9). Keep only the AMD loader + the editor lifecycle + value sync. Critically, set the base path to OtOpcUa's `_content`:
|
||||
```javascript
|
||||
(function () {
|
||||
const VS_BASE = "/_content/ZB.MOM.WW.OtOpcUa.AdminUI/lib/monaco/vs";
|
||||
const editors = {};
|
||||
let readyPromise = null;
|
||||
|
||||
function ensureLoaded() {
|
||||
if (readyPromise) return readyPromise;
|
||||
readyPromise = new Promise(function (resolve, reject) {
|
||||
const loader = document.createElement("script");
|
||||
loader.src = VS_BASE + "/loader.js";
|
||||
loader.onload = function () {
|
||||
require.config({ paths: { vs: VS_BASE } });
|
||||
require(["vs/editor/editor.main"], function () {
|
||||
registerCSharpProviders(); // no-op until Task 9 fills it in
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
loader.onerror = function (e) { reject(e); };
|
||||
document.head.appendChild(loader);
|
||||
});
|
||||
return readyPromise;
|
||||
}
|
||||
|
||||
function registerCSharpProviders() { /* Task 9: completions/hover/signature/format/inlay */ }
|
||||
|
||||
async function fetchDiagnostics(model) { return []; } // Task 9 wires the real fetch
|
||||
|
||||
async function createEditor(id, host, options, dotNetRef) {
|
||||
await ensureLoaded();
|
||||
if (!host) return;
|
||||
const editor = monaco.editor.create(host, {
|
||||
value: options.value || "", language: options.language || "csharp",
|
||||
theme: "vs", minimap: { enabled: false }, scrollBeyondLastLine: false,
|
||||
automaticLayout: true, fontSize: 13, lineNumbers: "on",
|
||||
renderLineHighlight: "line", readOnly: !!options.readOnly,
|
||||
tabSize: 4, insertSpaces: true, wordWrap: "off", fixedOverflowWidgets: true
|
||||
});
|
||||
let diagTimer = null;
|
||||
const scheduleDiagnostics = function () {
|
||||
if (diagTimer) clearTimeout(diagTimer);
|
||||
diagTimer = setTimeout(async function () {
|
||||
const model = editor.getModel(); if (!model) return;
|
||||
const markers = await fetchDiagnostics(model);
|
||||
monaco.editor.setModelMarkers(model, "otopcua", markers);
|
||||
dotNetRef.invokeMethodAsync("OnMarkersChanged", markers).catch(function () {});
|
||||
}, 500);
|
||||
};
|
||||
editor.onDidChangeModelContent(function () {
|
||||
dotNetRef.invokeMethodAsync("OnValueChanged", editor.getValue()).catch(function () {});
|
||||
if (options.language === "csharp") scheduleDiagnostics();
|
||||
});
|
||||
editors[id] = { editor: editor, dotNetRef: dotNetRef };
|
||||
if (options.language === "csharp") scheduleDiagnostics();
|
||||
}
|
||||
function setEditorOption(id, name, value) {
|
||||
const e = editors[id]; if (!e) return;
|
||||
if (name === "theme") { monaco.editor.setTheme(value); return; }
|
||||
const u = {}; u[name] = value; e.editor.updateOptions(u);
|
||||
}
|
||||
function format(id) { editors[id]?.editor.getAction("editor.action.formatDocument")?.run(); }
|
||||
function revealLine(id, line, col) {
|
||||
const e = editors[id]; if (!e) return;
|
||||
e.editor.revealLineInCenter(line); e.editor.setPosition({ lineNumber: line, column: col || 1 }); e.editor.focus();
|
||||
}
|
||||
function setValue(id, v) { const e = editors[id]; if (e && e.editor.getValue() !== v) e.editor.setValue(v || ""); }
|
||||
function getValue(id) { const e = editors[id]; return e ? e.editor.getValue() : null; }
|
||||
function setMarkers(id, m) { const e = editors[id]; const model = e && e.editor.getModel(); if (model) monaco.editor.setModelMarkers(model, "otopcua", m || []); }
|
||||
function dispose(id) { const e = editors[id]; if (!e) return; try { e.editor.dispose(); } catch (x) {} delete editors[id]; }
|
||||
|
||||
window.MonacoBlazor = { createEditor, setValue, getValue, setMarkers, setEditorOption, format, revealLine, dispose };
|
||||
// expose the editors map + helpers Task 9 will close over:
|
||||
window.__otopcuaEditors = editors;
|
||||
})();
|
||||
```
|
||||
(Task 9 reopens this file to add the providers + the real `fetchDiagnostics`. Keeping `editors` on `window.__otopcuaEditors` lets Task 9's providers find the model→editor mapping if needed; for v1 the providers don't need per-editor .NET context, so this may go unused — fine.)
|
||||
|
||||
**Step 3: Create `MonacoEditor.razor`** — port scadabridge's component but **strip all SCADA context** (`ScriptKind`, `DeclaredParameters`, `DeclaredParameterShapes`, `SiblingScripts`, `SelfAttributes`, `Children`, `Parent`, `GetContext`, the `ScadaContext` record). Keep: `Value`/`ValueChanged`, `Language="csharp"`, `Height`, `ReadOnly`, `ShowToolbar`, `MarkersChanged`, the toolbar (Format/Wrap/Minimap/Theme), `OnAfterRenderAsync` (create on first render + `setValue` on `Value` change), `[JSInvokable] OnValueChanged`/`OnMarkersChanged`, `RevealLineAsync`, `SafeInvokeAsync`, `DisposeAsync`. Namespace: `ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared`. Use a `DiagnosticMarker[]` parameter type for `OnMarkersChanged` (the DTO created in Task 2 — until then, type it `object[]` and tighten in Task 9, OR sequence Task 2 before this; simplest: keep `OnMarkersChanged(object[] markers)` here and let Task 9 finalize). The host div + create-options match scadabridge.
|
||||
|
||||
**Step 4: Wire the script in `App.razor`** — add before `<script src="_framework/blazor.web.js">`:
|
||||
```html
|
||||
<script src="_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-init.js"></script>
|
||||
```
|
||||
|
||||
**Step 5: Build**
|
||||
```bash
|
||||
dotnet build src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ZB.MOM.WW.OtOpcUa.AdminUI.csproj
|
||||
```
|
||||
Expected: succeeds.
|
||||
|
||||
**Step 6: Commit** (stage by path; the monaco folder is large but committed deliberately):
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/lib/monaco \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-init.js \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/MonacoEditor.razor \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/App.razor
|
||||
git commit -m "feat(adminui): vendor Monaco + reusable MonacoEditor component (no providers yet)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: ScriptAnalysis contracts + service analysis seam + endpoints + DI
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 1 (disjoint files)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisContracts.cs`
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs`
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisEndpoints.cs`
|
||||
- Modify: the AdminUI service-registration site (find `AddAdminUI`/`AddOtOpcUaAdminUI` extension, else the `hasAdmin` block in `src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs`)
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs` (~line 185, after `app.MapAdminUI<App>()`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/ScriptAnalysisServiceWrapperTests.cs`
|
||||
|
||||
**Step 1: Contracts** — port scadabridge's `ScriptAnalysisContracts.cs` but **drop** `ScriptKind` and every SCADA shape (`ScriptShape`, `ParameterShape`, `AttributeShape`, `CompositionContext`) and all the context fields on the requests. Final shapes:
|
||||
```csharp
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
public record DiagnoseRequest(string Code);
|
||||
public record DiagnoseResponse(IReadOnlyList<DiagnosticMarker> Markers);
|
||||
public record DiagnosticMarker(int Severity, int StartLineNumber, int StartColumn,
|
||||
int EndLineNumber, int EndColumn, string Message, string Code);
|
||||
|
||||
public record CompletionsRequest(string CodeText, int Line, int Column);
|
||||
public record CompletionsResponse(IReadOnlyList<CompletionItem> Items);
|
||||
public record CompletionItem(string Label, string InsertText, string Detail, string Kind, int InsertTextRules = 0);
|
||||
|
||||
public record HoverRequest(string CodeText, int Line, int Column);
|
||||
public record HoverResponse(string? Markdown);
|
||||
|
||||
public record SignatureHelpRequest(string CodeText, int Line, int Column);
|
||||
public record SignatureHelpResponse(string? Label, IReadOnlyList<SignatureHelpParameter>? Parameters, int ActiveParameter);
|
||||
public record SignatureHelpParameter(string Label, string? Documentation);
|
||||
|
||||
public record FormatRequest(string Code);
|
||||
public record FormatResponse(string Code);
|
||||
|
||||
public record InlayHintsRequest(string Code);
|
||||
public record InlayHintsResponse(IReadOnlyList<InlayHint> Hints);
|
||||
public record InlayHint(int Line, int Column, string Label);
|
||||
```
|
||||
|
||||
**Step 2: Service shell + the shared analysis seam.** Create `ScriptAnalysisService` with the wrapper/compilation core all methods share, plus empty public methods (filled in Tasks 3–8). Inject `IScriptTagCatalog` (interface created in Task 5; for now declare the ctor param and an interface stub — OR sequence Task 5 before Task 6 and leave the ctor parameterless until Task 6; simplest: parameterless ctor now, add `IScriptTagCatalog` in Task 6).
|
||||
```csharp
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting; // ScriptSandbox, ForbiddenTypeAnalyzer, DependencyExtractor
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions; // VirtualTagContext, ScriptGlobals (confirm namespace)
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
public sealed class ScriptAnalysisService
|
||||
{
|
||||
private static readonly SandboxConfig Sandbox = ScriptSandbox.Build(typeof(VirtualTagContext));
|
||||
private static readonly string Preamble = BuildPreamble();
|
||||
private static readonly CSharpCompilationOptions CompileOptions = new(
|
||||
OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release,
|
||||
allowUnsafe: false, warningLevel: 4, nullableContextOptions: NullableContextOptions.Enable);
|
||||
|
||||
private static string BuildPreamble()
|
||||
{
|
||||
var usings = string.Join("\n", Sandbox.Imports.Select(i => $"using {i};"));
|
||||
return usings + "\n\n" +
|
||||
"namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Compiled;\n" +
|
||||
"public static class CompiledScript\n{\n" +
|
||||
" public static object Run(ScriptGlobals<VirtualTagContext> globals)\n {\n" +
|
||||
" var ctx = globals.ctx;\n#line 1\n";
|
||||
// user source is appended after this, then " }\n}\n"
|
||||
}
|
||||
|
||||
/// <summary>Wrap user source in OtOpcUa's evaluator shape; returns the tree, semantic model, and the prefix length (chars before user source).</summary>
|
||||
private static (SyntaxTree Tree, SemanticModel Model, int Prefix) Analyze(string userSource)
|
||||
{
|
||||
var prefix = Preamble;
|
||||
var full = prefix + (userSource ?? "") + "\n }\n}\n";
|
||||
var tree = CSharpSyntaxTree.ParseText(full);
|
||||
var compilation = CSharpCompilation.Create("ScriptAnalysis", new[] { tree }, Sandbox.References, CompileOptions);
|
||||
return (tree, compilation.GetSemanticModel(tree), prefix.Length);
|
||||
}
|
||||
|
||||
/// <summary>(line,col) in user-source coords → physical offset in the wrapped document.</summary>
|
||||
private static int OffsetInWrapped(string userSource, int line, int column, int prefix)
|
||||
=> prefix + Math.Clamp(PositionToOffset(userSource, line, column), 0, (userSource ?? "").Length);
|
||||
|
||||
private static int PositionToOffset(string code, int line, int column)
|
||||
{
|
||||
int offset = 0, curLine = 1, curCol = 1;
|
||||
for (int i = 0; i < code.Length; i++)
|
||||
{
|
||||
if (curLine == line && curCol == column) return offset;
|
||||
if (code[i] == '\n') { curLine++; curCol = 1; } else { curCol++; }
|
||||
offset = i + 1;
|
||||
}
|
||||
return code.Length;
|
||||
}
|
||||
|
||||
public DiagnoseResponse Diagnose(DiagnoseRequest req) => new(Array.Empty<DiagnosticMarker>()); // Task 3
|
||||
public Task<CompletionsResponse> CompleteAsync(CompletionsRequest req) => Task.FromResult(new CompletionsResponse(Array.Empty<CompletionItem>())); // Tasks 4,6
|
||||
public HoverResponse Hover(HoverRequest req) => new((string?)null); // Task 7
|
||||
public SignatureHelpResponse SignatureHelp(SignatureHelpRequest req) => new(null, null, 0); // Task 7
|
||||
public FormatResponse Format(FormatRequest req) => new(req.Code); // Task 8
|
||||
public InlayHintsResponse InlayHints(InlayHintsRequest req) => new(Array.Empty<InlayHint>()); // Task 8 (stays empty)
|
||||
}
|
||||
```
|
||||
(Verify the exact namespace of `ScriptGlobals`/`VirtualTagContext` — the explorer reported `ZB.MOM.WW.OtOpcUa.Core.Scripting` for the types living under the `Scripting.Abstractions` project; match the actual `namespace` declarations.)
|
||||
|
||||
**Step 3: Endpoints** — port `ScriptAnalysisEndpoints.cs`, drop the `/run` route, swap auth:
|
||||
```csharp
|
||||
public static IEndpointRouteBuilder MapScriptAnalysisEndpoints(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
var group = endpoints.MapGroup("/api/script-analysis").RequireAuthorization("FleetAdmin");
|
||||
group.MapPost("/diagnostics", (DiagnoseRequest r, ScriptAnalysisService s) => Results.Ok(s.Diagnose(r)));
|
||||
group.MapPost("/completions", async (CompletionsRequest r, ScriptAnalysisService s) => Results.Ok(await s.CompleteAsync(r)));
|
||||
group.MapPost("/hover", (HoverRequest r, ScriptAnalysisService s) => Results.Ok(s.Hover(r)));
|
||||
group.MapPost("/signature-help", (SignatureHelpRequest r, ScriptAnalysisService s) => Results.Ok(s.SignatureHelp(r)));
|
||||
group.MapPost("/format", (FormatRequest r, ScriptAnalysisService s) => Results.Ok(s.Format(r)));
|
||||
group.MapPost("/inlay-hints", (InlayHintsRequest r, ScriptAnalysisService s) => Results.Ok(s.InlayHints(r)));
|
||||
return endpoints;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: DI** — register `services.AddScoped<ScriptAnalysisService>();` (and in Task 5, `services.AddScoped<IScriptTagCatalog, ScriptTagCatalog>();`) in the AdminUI service-registration extension.
|
||||
|
||||
**Step 5: Map** — in `Program.cs` `hasAdmin` block, after `app.MapAdminUI<App>();`:
|
||||
```csharp
|
||||
app.MapScriptAnalysisEndpoints();
|
||||
```
|
||||
|
||||
**Step 6: Test the wrapper seam** (the one piece of Task 2 with logic worth a test). Since `Analyze`/`OffsetInWrapped` are private, expose a tiny internal test hook OR test indirectly via Task 3. To keep Task 2 self-verifying, add an `internal` method `AnalyzeForTest(string) => Analyze(...)` guarded by `[assembly: InternalsVisibleTo("ZB.MOM.WW.OtOpcUa.AdminUI.Tests")]`, and assert that for source `return 1;` the compilation reports **no** user-span diagnostics and that a member-access after `ctx.` resolves to `VirtualTagContext`. (If you prefer, fold this verification into Task 3's diagnostics tests and keep Task 2 to a build-only check — acceptable.)
|
||||
|
||||
**Step 7: Build + commit**
|
||||
```bash
|
||||
dotnet build ZB.MOM.WW.OtOpcUa.slnx
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs \
|
||||
<the-service-registration-file> \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/ScriptAnalysisServiceWrapperTests.cs
|
||||
git commit -m "feat(adminui): script-analysis contracts, wrapper seam, endpoints + DI"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Diagnostics — Roslyn ∪ ForbiddenTypeAnalyzer ∪ DependencyExtractor
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 5 (disjoint files)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs` (`Diagnose`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/DiagnoseTests.cs`
|
||||
|
||||
**Step 1: Write failing tests** (xUnit + Shouldly):
|
||||
```csharp
|
||||
public sealed class DiagnoseTests
|
||||
{
|
||||
private static readonly ScriptAnalysisService Svc = new();
|
||||
private static IReadOnlyList<DiagnosticMarker> Diag(string code) => Svc.Diagnose(new DiagnoseRequest(code)).Markers;
|
||||
|
||||
[Fact] public void Clean_script_has_no_markers()
|
||||
=> Diag("""return (double)ctx.GetTag("A").Value * 2.0;""").ShouldBeEmpty();
|
||||
|
||||
[Fact] public void Roslyn_error_is_reported_on_the_user_line()
|
||||
{
|
||||
var m = Diag("return ctx.NoSuchMember();");
|
||||
m.ShouldNotBeEmpty();
|
||||
m[0].Severity.ShouldBe(8);
|
||||
m[0].StartLineNumber.ShouldBe(1); // #line 1 maps to user source
|
||||
}
|
||||
|
||||
[Fact] public void Forbidden_type_is_flagged()
|
||||
=> Diag("""var c = new System.Net.Http.HttpClient(); return 0;""")
|
||||
.ShouldContain(m => m.Message.Contains("HttpClient") || m.Code.StartsWith("OTSCRIPT") || m.Severity == 8);
|
||||
|
||||
[Fact] public void Dynamic_tag_path_is_flagged()
|
||||
{
|
||||
var m = Diag("""var p = "A"; return ctx.GetTag(p).Value;""");
|
||||
m.ShouldContain(x => x.Message.Contains("literal")); // DependencyExtractor rejection
|
||||
}
|
||||
|
||||
[Fact] public void Preamble_diagnostics_are_suppressed()
|
||||
=> Diag("var x = 1;").ShouldNotContain(m => m.Code == "CS0161"); // missing-return on Run() is hidden
|
||||
}
|
||||
```
|
||||
Run: `dotnet test tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests --filter FullyQualifiedName~DiagnoseTests` → FAIL.
|
||||
|
||||
**Step 2: Implement `Diagnose`:**
|
||||
```csharp
|
||||
public DiagnoseResponse Diagnose(DiagnoseRequest req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.Code)) return new DiagnoseResponse(Array.Empty<DiagnosticMarker>());
|
||||
var (tree, _, prefix) = Analyze(req.Code);
|
||||
var compilation = CSharpCompilation.Create("ScriptAnalysis", new[] { tree }, Sandbox.References, CompileOptions);
|
||||
var markers = new List<DiagnosticMarker>();
|
||||
|
||||
// (1) Roslyn — only diagnostics physically inside the user source (suppress preamble noise like CS0161 on Run()).
|
||||
foreach (var d in compilation.GetDiagnostics())
|
||||
{
|
||||
if (d.Severity < DiagnosticSeverity.Info || !d.Location.IsInSource) continue;
|
||||
if (d.Location.SourceSpan.Start < prefix) continue;
|
||||
markers.Add(ToMarker(d.Location.GetLineSpan(), Sev(d.Severity), d.GetMessage(), d.Id));
|
||||
}
|
||||
// (2) ForbiddenTypeAnalyzer — spans are in the wrapped tree; GetLineSpan honors #line → user coords.
|
||||
foreach (var r in ForbiddenTypeAnalyzer.Analyze(compilation))
|
||||
markers.Add(ToMarker(tree.GetLineSpan(r.Span), 8, r.Message, "OTSCRIPT_FORBIDDEN"));
|
||||
// (3) DependencyExtractor — spans are offsets in the *user* source; map directly.
|
||||
foreach (var r in DependencyExtractor.Extract(req.Code).Rejections)
|
||||
markers.Add(ToUserMarker(req.Code, r.Span, r.Message, "OTSCRIPT_DYNPATH"));
|
||||
|
||||
return new DiagnoseResponse(markers);
|
||||
}
|
||||
|
||||
private static int Sev(DiagnosticSeverity s) => s switch {
|
||||
DiagnosticSeverity.Error => 8, DiagnosticSeverity.Warning => 4, DiagnosticSeverity.Info => 2, _ => 1 };
|
||||
|
||||
private static DiagnosticMarker ToMarker(FileLinePositionSpan span, int sev, string msg, string code) => new(
|
||||
sev, span.StartLinePosition.Line + 1, span.StartLinePosition.Character + 1,
|
||||
span.EndLinePosition.Line + 1, span.EndLinePosition.Character + 1, msg, code);
|
||||
|
||||
private static DiagnosticMarker ToUserMarker(string userSource, Microsoft.CodeAnalysis.Text.TextSpan span, string msg, string code)
|
||||
{
|
||||
var (sl, sc) = LineCol(userSource, span.Start);
|
||||
var (el, ec) = LineCol(userSource, span.End);
|
||||
return new(8, sl, sc, el, ec, msg, code);
|
||||
}
|
||||
private static (int, int) LineCol(string code, int offset)
|
||||
{
|
||||
int line = 1, col = 1;
|
||||
for (int i = 0; i < offset && i < code.Length; i++)
|
||||
if (code[i] == '\n') { line++; col = 1; } else col++;
|
||||
return (line, col);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Run tests** → PASS. (If `ForbiddenTypeAnalyzer` reports nothing for `HttpClient` because the reference set already excludes `System.Net.Http`, the Roslyn pass will instead emit a CS-level "type/namespace not found" error in user-span — the test's `||` covers that. Confirm which path fires and tighten the assert.)
|
||||
|
||||
**Step 4: Commit**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/DiagnoseTests.cs
|
||||
git commit -m "feat(adminui): script diagnostics (Roslyn + forbidden-type + dynamic-path)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Completions — scope + dot-member
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~4 min
|
||||
**Parallelizable with:** Task 5 (disjoint files)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs` (`CompleteAsync` + `ToCompletionItem` + `TryGetDotMembers`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/CompletionTests.cs`
|
||||
|
||||
**Step 1: Failing tests:**
|
||||
```csharp
|
||||
public sealed class CompletionTests
|
||||
{
|
||||
private static readonly ScriptAnalysisService Svc = new();
|
||||
private static async Task<IReadOnlyList<CompletionItem>> Complete(string code, int line, int col)
|
||||
=> (await Svc.CompleteAsync(new CompletionsRequest(code, line, col))).Items;
|
||||
|
||||
[Fact] public async Task Dot_after_ctx_offers_context_members()
|
||||
{
|
||||
var items = await Complete("ctx.", 1, 5); // caret right after "ctx."
|
||||
var labels = items.Select(i => i.Label).ToList();
|
||||
labels.ShouldContain("GetTag");
|
||||
labels.ShouldContain("SetVirtualTag");
|
||||
labels.ShouldContain("Now");
|
||||
}
|
||||
|
||||
[Fact] public async Task Scope_completion_includes_ctx_local()
|
||||
=> (await Complete("c", 1, 2)).Select(i => i.Label).ShouldContain("ctx");
|
||||
}
|
||||
```
|
||||
Run filter `~CompletionTests` → FAIL.
|
||||
|
||||
**Step 2: Implement** — port scadabridge's `CompleteAsync` core, but build the document via `Analyze(...)` and map the offset via `OffsetInWrapped(...)`; the string-literal branch is added in Task 6 (leave a `// Task 6: tag-path literals` placeholder):
|
||||
```csharp
|
||||
public async Task<CompletionsResponse> CompleteAsync(CompletionsRequest req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.CodeText)) return new CompletionsResponse(Array.Empty<CompletionItem>());
|
||||
var (tree, model, prefix) = Analyze(req.CodeText);
|
||||
var position = OffsetInWrapped(req.CodeText, req.Line, req.Column, prefix);
|
||||
var root = await tree.GetRootAsync();
|
||||
var token = root.FindToken(Math.Max(0, position - 1));
|
||||
|
||||
// Task 6: if token is a string literal inside ctx.GetTag/SetVirtualTag → tag-path completion.
|
||||
|
||||
var dot = TryGetDotMembers(token, model);
|
||||
if (dot != null) return new CompletionsResponse(dot);
|
||||
|
||||
var scoped = model.LookupSymbols(position)
|
||||
.Where(s => !s.IsImplicitlyDeclared && !string.IsNullOrEmpty(s.Name))
|
||||
.GroupBy(s => s.Name).Select(g => g.First())
|
||||
.Select(ToCompletionItem).Take(200).ToList();
|
||||
return new CompletionsResponse(scoped);
|
||||
}
|
||||
```
|
||||
Copy `TryGetDotMembers` and `ToCompletionItem` verbatim from scadabridge (`ScriptAnalysisService.cs:885,1271`) — they are pure Roslyn, no domain coupling.
|
||||
|
||||
**Step 3: Run tests** → PASS.
|
||||
|
||||
**Step 4: Commit**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/CompletionTests.cs
|
||||
git commit -m "feat(adminui): scope + dot-member script completions"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: IScriptTagCatalog (tag + virtual-tag path provider)
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 3, Task 4 (disjoint files)
|
||||
|
||||
**Files:**
|
||||
- Create: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/IScriptTagCatalog.cs` (+ `ScriptTagCatalog`)
|
||||
- Modify: AdminUI service registration (`AddScoped<IScriptTagCatalog, ScriptTagCatalog>()`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/ScriptTagCatalogTests.cs`
|
||||
|
||||
**Step 0 (verify the canonical path format — do this FIRST):** Read how the runtime resolves a `ctx.GetTag("…")` literal to a configured tag (the virtual-tag engine's subscription/read-cache key construction, and how the equipment-namespace browse path is built). Confirm the exact separator and segment order a script author types — i.e. whether it is `Area/Line/Equipment/Name`, `Equipment/Name`, or `FolderPath/Name`. **Match the catalog's projected strings to that.** If the format genuinely can't be pinned down, project both the leaf `Name` and a best-effort full path and note the ambiguity in a code comment + the task report (do NOT silently guess).
|
||||
|
||||
**Step 1: Failing test** (in-memory EF — see `Microsoft.EntityFrameworkCore.InMemory`, already referenced by AdminUI.Tests):
|
||||
```csharp
|
||||
public sealed class ScriptTagCatalogTests
|
||||
{
|
||||
private static OtOpcUaConfigDbContext NewDb()
|
||||
{
|
||||
var opts = new DbContextOptionsBuilder<OtOpcUaConfigDbContext>()
|
||||
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
|
||||
return new OtOpcUaConfigDbContext(opts);
|
||||
}
|
||||
|
||||
[Fact] public async Task Returns_configured_tag_and_virtual_tag_paths()
|
||||
{
|
||||
await using var db = NewDb();
|
||||
// seed one Equipment + one Tag + one VirtualTag (use the confirmed path convention from Step 0)
|
||||
// ...
|
||||
await db.SaveChangesAsync();
|
||||
var catalog = new ScriptTagCatalog(/* IDbContextFactory or db */);
|
||||
var paths = await catalog.GetPathsAsync(null, default);
|
||||
paths.ShouldContain(/* the expected tag path */);
|
||||
paths.ShouldContain(/* the expected virtual-tag path */);
|
||||
}
|
||||
|
||||
[Fact] public async Task Filter_prefix_narrows_results() { /* assert prefix filtering */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Implement:**
|
||||
```csharp
|
||||
public interface IScriptTagCatalog
|
||||
{
|
||||
Task<IReadOnlyList<string>> GetPathsAsync(string? filter, CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> dbf) : IScriptTagCatalog
|
||||
{
|
||||
public async Task<IReadOnlyList<string>> GetPathsAsync(string? filter, CancellationToken ct)
|
||||
{
|
||||
await using var db = await dbf.CreateDbContextAsync(ct);
|
||||
// Build paths per the Step 0 convention. Pull the minimal columns, project to the
|
||||
// path string, distinct, filtered by `filter` (StartsWith/Contains, case-insensitive),
|
||||
// capped (e.g. Take(200)) so completion stays responsive.
|
||||
// ...
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
(Use `IDbContextFactory<OtOpcUaConfigDbContext>` to match `UnsTreeService`'s pattern.)
|
||||
|
||||
**Step 3: Run tests** → PASS. **Step 4: Register in DI. Step 5: Commit.**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/IScriptTagCatalog.cs \
|
||||
<service-registration-file> \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/ScriptTagCatalogTests.cs
|
||||
git commit -m "feat(adminui): IScriptTagCatalog for tag-path completion"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Tag-path string-literal completion
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~4 min
|
||||
**Parallelizable with:** none (edits ScriptAnalysisService.cs; needs Tasks 4 + 5)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/ScriptAnalysis/ScriptAnalysisService.cs` (inject `IScriptTagCatalog`, fill the Task-6 placeholder in `CompleteAsync`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/TagPathCompletionTests.cs`
|
||||
|
||||
**Step 1: Failing test** with a fake catalog:
|
||||
```csharp
|
||||
private sealed class FakeCatalog : IScriptTagCatalog
|
||||
{
|
||||
public Task<IReadOnlyList<string>> GetPathsAsync(string? f, CancellationToken ct)
|
||||
=> Task.FromResult<IReadOnlyList<string>>(new[] { "Line1/Speed", "Line1/Temp" });
|
||||
}
|
||||
|
||||
[Fact] public async Task String_literal_in_GetTag_offers_tag_paths()
|
||||
{
|
||||
var svc = new ScriptAnalysisService(new FakeCatalog());
|
||||
var items = (await svc.CompleteAsync(new CompletionsRequest("""ctx.GetTag("")""", 1, 12))).Items; // caret inside ""
|
||||
items.Select(i => i.Label).ShouldContain("Line1/Speed");
|
||||
}
|
||||
```
|
||||
(Adjust the column to land inside the empty string literal — count: `ctx.GetTag("` is 12 chars, so column 13 is between the quotes; verify with the test.)
|
||||
|
||||
**Step 2: Implement** the placeholder: when `token.IsKind(SyntaxKind.StringLiteralToken)` and its enclosing `InvocationExpressionSyntax` is a `MemberAccessExpressionSyntax` named `GetTag`/`SetVirtualTag` on `ctx`, fetch `await _catalog.GetPathsAsync(token.ValueText, ct)` and map to `CompletionItem(label: path, insertText: path, detail: "tag path", kind: "Field")`. Add the ctor `public ScriptAnalysisService(IScriptTagCatalog catalog)` and store it; update DI/tests that `new ScriptAnalysisService()` to pass a catalog (the no-arg tests in Tasks 3/4 can switch to a `FakeCatalog` or you add a parameterless ctor that defers — prefer requiring the catalog and updating those test fixtures).
|
||||
|
||||
**Step 3: Run the full AdminUI.Tests** → green. **Step 4: Commit.**
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Hover + signature help
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~4 min
|
||||
**Parallelizable with:** none (edits ScriptAnalysisService.cs)
|
||||
|
||||
**Files:**
|
||||
- Modify: `ScriptAnalysisService.cs` (`Hover`, `SignatureHelp`)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/HoverSignatureTests.cs`
|
||||
|
||||
**Step 1: Failing tests** — hovering `GetTag` returns markdown containing `DataValueSnapshot`/`GetTag`; signature help inside `ctx.GetTag(` returns a label mentioning `path`.
|
||||
|
||||
**Step 2: Implement** using the semantic model from `Analyze(...)`:
|
||||
- **Hover:** `model.GetSymbolInfo(node)` / `GetTypeInfo` at the mapped offset; format `symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)` + XML doc summary if present into markdown. (Simpler than scadabridge's domain hover — no `Parameters[...]` branch.)
|
||||
- **SignatureHelp:** find the enclosing `InvocationExpressionSyntax`; resolve the called method symbol via `model.GetSymbolInfo(invocation.Expression)` (handle `OverloadResolutionFailure` candidates); build `SignatureHelpResponse` from the method's parameters; `ActiveParameter` = commas-before-cursor in the argument list. Reuse scadabridge's comma-counting/active-index logic (`ScriptAnalysisService.cs:840`).
|
||||
|
||||
**Step 3: Tests PASS. Step 4: Commit.**
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Format (+ InlayHints stub)
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~3 min
|
||||
**Parallelizable with:** none (edits ScriptAnalysisService.cs)
|
||||
|
||||
**Files:**
|
||||
- Modify: `ScriptAnalysisService.cs` (`Format`; leave `InlayHints` empty as scadabridge does)
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/FormatTests.cs`
|
||||
|
||||
**Step 1: Failing test** — `Format(new FormatRequest("var x=1;return x;"))` returns code containing newlines / canonical spacing (`!= input`, parses clean).
|
||||
|
||||
**Step 2: Implement** — copy scadabridge's `Format` verbatim (`CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script))` → `root.NormalizeWhitespace(" ", "\n").ToFullString()`, try/catch returns input). `InlayHints` stays `new(Array.Empty<InlayHint>())`.
|
||||
|
||||
**Step 3: Test PASS. Step 4: Commit.**
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Wire the 6 Monaco language providers in monaco-init.js
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 10, Task 11 (disjoint files); needs Tasks 2–8 endpoints live
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-init.js` (`registerCSharpProviders`, `fetchDiagnostics`)
|
||||
|
||||
**Step 1:** Port scadabridge's six provider registrations + `fetchDiagnostics`, but **strip the SCADA context** (`lookupContext`, `GetContext`, all `declaredParameters/siblingScripts/...` body fields). Request bodies become exactly the v1 DTO shapes:
|
||||
- completions → `{ codeText: model.getValue(), line, column }`
|
||||
- hover → `{ codeText, line, column }`
|
||||
- signature-help → `{ codeText, line, column }`
|
||||
- format → `{ code: model.getValue() }`
|
||||
- inlay-hints → `{ code: model.getValue() }`
|
||||
- diagnostics → `{ code: model.getValue() }`
|
||||
|
||||
Keep `KIND_MAP`, the snippet-range handling, the response→Monaco mapping, and `monaco.editor.setModelMarkers(model, "otopcua", markers)`. Each provider keeps the `try/catch → return empty` guard so a transient endpoint failure degrades gracefully.
|
||||
|
||||
**Step 2: Build (no test — JS).** **Step 3: Commit.**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-init.js
|
||||
git commit -m "feat(adminui): wire Monaco language providers to /api/script-analysis"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Swap ScriptEdit page to MonacoEditor
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~4 min
|
||||
**Parallelizable with:** Task 11 (disjoint files); needs Task 1
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor`
|
||||
- Delete: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js`
|
||||
|
||||
**Step 1:** Replace the `<InputTextArea id="script-source" @bind-Value="_form.SourceCode" .../>` with:
|
||||
```razor
|
||||
<MonacoEditor @bind-Value="_form.SourceCode" Height="420px" />
|
||||
```
|
||||
(`@bind-Value` wires `Value` + `ValueChanged`.) Remove the `OnAfterRenderAsync` block that injected `monaco-loader.js` via `JS.InvokeVoidAsync("eval", ...)` + `Task.Delay(50)` + `otOpcUaScriptEditor.attach`, and the now-unused `IJSRuntime`/`_loaded` plumbing if nothing else uses them. Keep the SHA-256-on-save path untouched.
|
||||
|
||||
**Step 2:** `git rm src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js`.
|
||||
|
||||
**Step 3: Build.** **Step 4: Commit.**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor
|
||||
git rm src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js
|
||||
git commit -m "feat(adminui): ScriptEdit uses MonacoEditor; drop CDN loader"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 11: VirtualTagModal inline script-source panel
|
||||
|
||||
**Classification:** standard
|
||||
**Estimated implement time:** ~5 min
|
||||
**Parallelizable with:** Task 10 (disjoint files); needs Task 1
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs` + `UnsTreeService.cs` (3 new methods)
|
||||
- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/VirtualTagModal.razor`
|
||||
- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/ScriptSourceServiceTests.cs`
|
||||
|
||||
**Step 1: Failing service tests** (in-memory EF): `GetScriptSourceAsync(scriptId)` returns `(SourceCode, RowVersion, Name)`; `CountVirtualTagsUsingScriptAsync(scriptId)` returns the number of `VirtualTags` with that `ScriptId`; `UpdateScriptSourceAsync(scriptId, source, rowVersion)` recomputes the SHA-256 `SourceHash`, succeeds on a matching RowVersion, and returns a concurrency error on a stale one.
|
||||
|
||||
**Step 2: Implement** the 3 service methods (mirror the existing `LoadScriptsAsync`/Update patterns in `UnsTreeService`; reuse the page's existing `HashSource`/SHA-256 helper — extract it to the service so both call sites share it). Result type: reuse the existing `(bool Ok, string? Error)`-style result used by `UpdateTagAsync`.
|
||||
|
||||
**Step 3:** In `VirtualTagModal.razor`, below the Script dropdown, add a collapsible **"Script source"** panel shown when a script is selected:
|
||||
- On script selection (or modal open with an existing `ScriptId`), call `GetScriptSourceAsync` + `CountVirtualTagsUsingScriptAsync` and show a notice: `Editing shared script "{Name}" — used by {N} virtual tag(s). Changes affect all of them.`
|
||||
- `<MonacoEditor @bind-Value="_scriptSource" Height="300px" />`
|
||||
- A dedicated **Save script** button calling `UpdateScriptSourceAsync(scriptId, _scriptSource, _scriptRowVersion)`; on success refresh `_scriptRowVersion` + show a saved indicator; on concurrency error show the standard reload message. This Save is **separate** from the virtual-tag Create/Save (`SaveAsync`) — it does not touch `_form`.
|
||||
|
||||
**Step 4: Tests green; build.** **Step 5: Commit.**
|
||||
```bash
|
||||
git add src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IUnsTreeService.cs \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/UnsTreeService.cs \
|
||||
src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Uns/VirtualTagModal.razor \
|
||||
tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/ScriptSourceServiceTests.cs
|
||||
git commit -m "feat(adminui): inline script-source editor in the virtual-tag modal"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 12: Live verification in docker-dev
|
||||
|
||||
**Classification:** verification (no code)
|
||||
**Estimated implement time:** ~5 min (interactive; user signs in)
|
||||
**Parallelizable with:** none (needs all prior tasks)
|
||||
|
||||
**Files:** none.
|
||||
|
||||
**Steps:**
|
||||
1. Rebuild central in docker-dev:
|
||||
```bash
|
||||
docker compose -f docker-dev/docker-compose.yml build central-1
|
||||
docker compose -f docker-dev/docker-compose.yml up -d --no-deps --force-recreate central-1 central-2
|
||||
```
|
||||
2. **Ask the user to sign in** to the AdminUI (the agent must NOT enter credentials).
|
||||
3. Drive the browser (mcp__claude-in-chrome) and verify on `/scripts/{id}`:
|
||||
- Editor renders (Monaco, line numbers, theme); typing two-way binds.
|
||||
- Type `ctx.` → completion lists `GetTag`/`SetVirtualTag`/`Now`/`Logger`.
|
||||
- Type a bad line (`ctx.Nope();`) → red squiggle on the right line.
|
||||
- `ctx.GetTag("` → tag-path suggestions appear.
|
||||
- Hover `GetTag` → type info; Format button reflows; theme toggle works.
|
||||
4. Verify the VirtualTagModal: open it, pick a script → the "Script source" panel loads with the "used by N" notice; edit + Save script; reopen to confirm persistence.
|
||||
5. Reset the rig to baseline (remove any test artifacts created).
|
||||
|
||||
Report the results (with screenshots). Done = build clean + `dotnet test ZB.MOM.WW.OtOpcUa.slnx` green + this `/run` pass.
|
||||
|
||||
---
|
||||
|
||||
### Task 13: Docs + memory + finish
|
||||
|
||||
**Classification:** small
|
||||
**Estimated implement time:** ~4 min
|
||||
**Parallelizable with:** none
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/ScriptEditor.md` (the Monaco/Roslyn editor: architecture, endpoints, how to extend completions, vendoring/air-gap, the wrapper-fidelity notes)
|
||||
- Modify: `docs/README.md` (index row), `CLAUDE.md` (a short "Scripting / script editor" note pointing at `docs/ScriptEditor.md`)
|
||||
- Memory: update `MEMORY.md` + a new `project_monaco_script_editor.md` (architecture + the `#line`/offset-mapping + preamble-suppression gotchas + the tag-path-format finding from Task 5 Step 0)
|
||||
|
||||
**Steps:** write the docs, update the index + CLAUDE.md, save memory, build clean, commit. Then run **superpowers-extended-cc:finishing-a-development-branch** to merge `feat/monaco-script-editor` → `master`.
|
||||
|
||||
---
|
||||
|
||||
## Execution order & parallelism summary
|
||||
|
||||
```
|
||||
Task 0 (refs+branch)
|
||||
├─ Task 1 (frontend: vendor + MonacoEditor + minimal init) ∥ Task 2 (backend: contracts+seam+endpoints+DI)
|
||||
Task 2 ─┬─ Task 3 (diagnostics) ∥ Task 5 (IScriptTagCatalog)
|
||||
├─ Task 4 (completions) ∥ Task 5
|
||||
├─ Task 6 (tag-path completion) ← needs 4 + 5
|
||||
├─ Task 7 (hover + signature)
|
||||
└─ Task 8 (format + inlay)
|
||||
Task 9 (wire JS providers) ← needs 2–8 endpoints ∥ Task 10 (ScriptEdit swap) ∥ Task 11 (modal panel)
|
||||
Task 12 (live /run) ← needs all
|
||||
Task 13 (docs + finish)
|
||||
```
|
||||
Note: Tasks 3,4,6,7,8 all edit `ScriptAnalysisService.cs` — serialize them (not mutually parallelizable) even though their test files are disjoint. Task 5 (catalog) and Task 1 (frontend) are the genuine cross-cutting parallel opportunities.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"planPath": "docs/plans/2026-06-09-monaco-script-editor.md",
|
||||
"branch": "feat/monaco-script-editor",
|
||||
"status": "complete",
|
||||
"tasks": [
|
||||
{"id": 163, "planTask": 0, "subject": "Task 0: AdminUI project references + feature branch", "status": "completed", "commit": "a2dbc5e"},
|
||||
{"id": 164, "planTask": 1, "subject": "Task 1: Vendor Monaco + MonacoEditor.razor + minimal monaco-init.js + App.razor", "status": "completed", "commit": "9afb2d2"},
|
||||
{"id": 165, "planTask": 2, "subject": "Task 2: ScriptAnalysis contracts + service seam + endpoints + DI", "status": "completed", "commit": "b54a6ad"},
|
||||
{"id": 166, "planTask": 3, "subject": "Task 3: Diagnostics (Roslyn + ForbiddenTypeAnalyzer + DependencyExtractor)", "status": "completed", "commit": "6a9b052"},
|
||||
{"id": 167, "planTask": 4, "subject": "Task 4: Completions (scope + dot-member)", "status": "completed", "commit": "93f5a74"},
|
||||
{"id": 168, "planTask": 5, "subject": "Task 5: IScriptTagCatalog (tag + virtual-tag path provider)", "status": "completed", "commit": "d143493"},
|
||||
{"id": 169, "planTask": 6, "subject": "Task 6: Tag-path string-literal completion", "status": "completed", "commit": "521fb61"},
|
||||
{"id": 170, "planTask": 7, "subject": "Task 7: Hover + signature help", "status": "completed", "commit": "9104b6c"},
|
||||
{"id": 171, "planTask": 8, "subject": "Task 8: Format (+ InlayHints stub)", "status": "completed", "commit": "4a2f7e3"},
|
||||
{"id": 172, "planTask": 9, "subject": "Task 9: Wire 6 Monaco language providers in monaco-init.js", "status": "completed", "commit": "071bed5"},
|
||||
{"id": 173, "planTask": 10, "subject": "Task 10: Swap ScriptEdit page to MonacoEditor", "status": "completed", "commit": "088fc50"},
|
||||
{"id": 174, "planTask": 11, "subject": "Task 11: VirtualTagModal inline script-source panel", "status": "completed", "commit": "fc7dc3b"},
|
||||
{"id": 175, "planTask": 12, "subject": "Task 12: Live verification in docker-dev", "status": "completed", "note": "all checks passed: render, completion, diagnostics, format, tag-path, modal panel"},
|
||||
{"id": 176, "planTask": 13, "subject": "Task 13: Docs + memory + finish branch", "status": "completed", "commit": "4d12088"}
|
||||
],
|
||||
"lastUpdated": "2026-06-09"
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
<Routes/>
|
||||
<script src="_content/ZB.MOM.WW.OtOpcUa.AdminUI/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<ThemeScripts />
|
||||
<script src="_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-init.js"></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
@using ZB.MOM.WW.OtOpcUa.Configuration.Entities
|
||||
@inject IDbContextFactory<OtOpcUaConfigDbContext> DbFactory
|
||||
@inject NavigationManager Nav
|
||||
@inject IJSRuntime JS
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">@(IsNew ? "New script" : "Edit script")</h4>
|
||||
@@ -57,15 +56,8 @@ else
|
||||
<section class="panel rise mt-3">
|
||||
<div class="panel-head">Source</div>
|
||||
<div style="padding:1rem">
|
||||
@* The textarea stays in the DOM and remains Blazor's source of truth. Monaco
|
||||
mounts a <div> beside it (textarea hides), and the loader's onDidChangeModelContent
|
||||
handler mirrors edits back into the textarea + fires the input event so @bind
|
||||
picks them up. Falls back to the textarea gracefully if Monaco's CDN is
|
||||
unreachable (air-gapped deployments — see monaco-loader.js). *@
|
||||
<InputTextArea id="script-source" @bind-Value="_form.SourceCode"
|
||||
class="form-control form-control-sm mono" rows="20"
|
||||
placeholder="// C# expression body" />
|
||||
<div class="form-text">SHA-256 hash is computed automatically on save. Monaco editor attaches over the textarea on render.</div>
|
||||
<MonacoEditor @bind-Value="_form.SourceCode" Height="420px" />
|
||||
<div class="form-text">SHA-256 hash is computed automatically on save.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -110,24 +102,6 @@ else
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender || !_loaded) return;
|
||||
// Inject loader once, then attach over the textarea. Failures are silent — the page
|
||||
// is fully usable via the underlying textarea if Monaco's CDN is unreachable.
|
||||
try
|
||||
{
|
||||
await JS.InvokeVoidAsync("eval", "if (!document.querySelector('script[data-otopcua=monaco-loader]')) { var s=document.createElement('script'); s.src='/_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-loader.js'; s.dataset.otopcua='monaco-loader'; document.head.appendChild(s); }");
|
||||
// Wait a tick for the loader IIFE to register window.otOpcUaScriptEditor, then attach.
|
||||
await Task.Delay(50);
|
||||
await JS.InvokeVoidAsync("otOpcUaScriptEditor.attach", "script-source");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Textarea remains the editor — no-op.
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SubmitAsync()
|
||||
{
|
||||
_busy = true; _error = null;
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
else
|
||||
{
|
||||
<section class="panel notice rise" style="animation-delay:.02s">
|
||||
Scripts are fleet-wide expression compilations referenced by virtual tags and scripted
|
||||
alarms. The default language is C#; expansion of the editor (Monaco syntax, dependency
|
||||
introspection) lands in Phase D.2.
|
||||
Scripts are fleet-wide C# expression compilations referenced by virtual tags and scripted
|
||||
alarms. The editor provides Roslyn-backed IntelliSense — completions, diagnostics, hover,
|
||||
and tag-path suggestions for <code>ctx.GetTag</code> / <code>ctx.SetVirtualTag</code> literals.
|
||||
</section>
|
||||
|
||||
<section class="panel rise mt-3" style="animation-delay:.08s">
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
@namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared
|
||||
@using Microsoft.Extensions.Logging
|
||||
@implements IAsyncDisposable
|
||||
@inject IJSRuntime JS
|
||||
@inject Microsoft.Extensions.Logging.ILogger<MonacoEditor> Logger
|
||||
|
||||
@if (ShowToolbar)
|
||||
{
|
||||
<div class="d-flex justify-content-end align-items-center gap-3 mb-1 small text-muted">
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none" @onclick="FormatAsync"
|
||||
title="Format document (Ctrl/Cmd+Shift+F)">Format</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none" @onclick="ToggleWrap"
|
||||
title="Word wrap">@(_wrap ? "Wrap on" : "Wrap off")</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none" @onclick="ToggleMinimap"
|
||||
title="Toggle minimap">@(_minimap ? "Minimap on" : "Minimap off")</button>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none" @onclick="ToggleTheme"
|
||||
title="Toggle theme">@(_dark ? "Dark" : "Light")</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div @ref="_hostRef" class="monaco-editor-host"
|
||||
style="height: @Height; border: 1px solid var(--bs-border-color); border-radius: 0.25rem; overflow: hidden;"></div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string Value { get; set; } = "";
|
||||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||
[Parameter] public string Language { get; set; } = "csharp";
|
||||
[Parameter] public string Height { get; set; } = "320px";
|
||||
[Parameter] public bool ReadOnly { get; set; } = false;
|
||||
[Parameter] public bool ShowToolbar { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Fires whenever Monaco's marker set updates (after the 500 ms diagnostic
|
||||
/// debounce). The marker DTO is not modelled yet — typed as object[] until a
|
||||
/// later task wires the Roslyn-backed diagnostics.
|
||||
/// </summary>
|
||||
[Parameter] public EventCallback<object[]> MarkersChanged { get; set; }
|
||||
|
||||
private ElementReference _hostRef;
|
||||
private DotNetObjectReference<MonacoEditor>? _dotNetRef;
|
||||
private readonly string _id = Guid.NewGuid().ToString("N");
|
||||
private string _lastSentValue = "";
|
||||
private bool _initialized;
|
||||
private bool _wrap;
|
||||
private bool _minimap;
|
||||
private bool _dark;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
_dotNetRef = DotNetObjectReference.Create(this);
|
||||
_lastSentValue = Value ?? "";
|
||||
try
|
||||
{
|
||||
await JS.InvokeVoidAsync(
|
||||
"MonacoBlazor.createEditor",
|
||||
_id,
|
||||
_hostRef,
|
||||
new
|
||||
{
|
||||
value = Value ?? "",
|
||||
language = Language,
|
||||
readOnly = ReadOnly
|
||||
},
|
||||
_dotNetRef);
|
||||
_initialized = true;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// Prerendering: JS interop is not available yet — the next
|
||||
// (interactive) render retries. Expected, not logged.
|
||||
}
|
||||
catch (JSDisconnectedException)
|
||||
{
|
||||
// Circuit disconnected before init completed — nothing to do.
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
// A genuine Monaco init failure — surface it instead of hiding it.
|
||||
Logger.LogError(ex, "Monaco editor {EditorId} failed to initialize.", _id);
|
||||
}
|
||||
}
|
||||
else if (_initialized && (Value ?? "") != _lastSentValue)
|
||||
{
|
||||
_lastSentValue = Value ?? "";
|
||||
await SafeInvokeAsync("MonacoBlazor.setValue", "set editor value", _id, _lastSentValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a Monaco JS function, swallowing the expected disconnect case but
|
||||
/// logging any genuine JS error so failures are not silent.
|
||||
/// </summary>
|
||||
private async ValueTask SafeInvokeAsync(string fn, string action, params object?[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JS.InvokeVoidAsync(fn, args);
|
||||
}
|
||||
catch (JSDisconnectedException)
|
||||
{
|
||||
// Circuit gone — the editor no longer exists; nothing to log.
|
||||
}
|
||||
catch (JSException ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Monaco editor {EditorId}: failed to {Action}.", _id, action);
|
||||
}
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public Task OnValueChanged(string newValue)
|
||||
{
|
||||
var normalized = newValue ?? "";
|
||||
if (normalized == _lastSentValue)
|
||||
return Task.CompletedTask;
|
||||
_lastSentValue = normalized;
|
||||
return ValueChanged.InvokeAsync(_lastSentValue);
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public Task OnMarkersChanged(object[] markers) =>
|
||||
MarkersChanged.InvokeAsync(markers ?? Array.Empty<object>());
|
||||
|
||||
/// <summary>Programmatic scroll-to-line (called by the problems panel).</summary>
|
||||
public async Task RevealLineAsync(int line, int column = 1)
|
||||
{
|
||||
if (!_initialized) return;
|
||||
await SafeInvokeAsync("MonacoBlazor.revealLine", "reveal line", _id, line, column);
|
||||
}
|
||||
|
||||
private async Task FormatAsync()
|
||||
{
|
||||
if (!_initialized) return;
|
||||
await SafeInvokeAsync("MonacoBlazor.format", "format document", _id);
|
||||
}
|
||||
|
||||
private async Task ToggleWrap()
|
||||
{
|
||||
_wrap = !_wrap;
|
||||
await SafeInvokeAsync("MonacoBlazor.setEditorOption", "toggle word wrap", _id, "wordWrap", _wrap ? "on" : "off");
|
||||
}
|
||||
|
||||
private async Task ToggleMinimap()
|
||||
{
|
||||
_minimap = !_minimap;
|
||||
await SafeInvokeAsync("MonacoBlazor.setEditorOption", "toggle minimap", _id, "minimap", new { enabled = _minimap });
|
||||
}
|
||||
|
||||
private async Task ToggleTheme()
|
||||
{
|
||||
_dark = !_dark;
|
||||
await SafeInvokeAsync("MonacoBlazor.setEditorOption", "toggle theme", _id, "theme", _dark ? "vs-dark" : "vs");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
// Disposal commonly races a circuit disconnect — JSDisconnectedException
|
||||
// here is expected and silent; a real JSException is still logged.
|
||||
await SafeInvokeAsync("MonacoBlazor.dispose", "dispose editor", _id);
|
||||
}
|
||||
_dotNetRef?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,8 @@
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label" for="vtag-script">Script</label>
|
||||
<InputSelect id="vtag-script" @bind-Value="_form.ScriptId" class="form-select form-select-sm">
|
||||
<InputSelect id="vtag-script" @bind-Value="_form.ScriptId"
|
||||
@bind-Value:after="OnScriptChangedAsync" class="form-select form-select-sm">
|
||||
<option value="">— pick script —</option>
|
||||
@foreach (var (id, display) in Scripts)
|
||||
{
|
||||
@@ -57,6 +58,48 @@
|
||||
<ValidationMessage For="@(() => _form.ScriptId)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Inline script-source editor. Shown only when a script is bound. This panel saves
|
||||
the SHARED Script row on its OWN concurrency-guarded button — it is deliberately
|
||||
separate from the virtual-tag Create/Save below and never touches _form or closes
|
||||
the modal. *@
|
||||
@if (!string.IsNullOrEmpty(_form.ScriptId) && _scriptLoaded)
|
||||
{
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold">Script source</span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 text-decoration-none"
|
||||
@onclick="ToggleScriptPanel">
|
||||
@(_scriptExpanded ? "Hide" : "Edit source")
|
||||
</button>
|
||||
</div>
|
||||
@if (_scriptExpanded)
|
||||
{
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning py-2 small mb-2">
|
||||
Editing shared script "<span class="mono">@_scriptName</span>" — used by
|
||||
@_scriptUsageCount virtual tag(s). Changes affect all of them.
|
||||
</div>
|
||||
<MonacoEditor @bind-Value="_scriptSource" Height="300px" />
|
||||
@if (!string.IsNullOrWhiteSpace(_scriptError))
|
||||
{
|
||||
<div class="text-danger small mt-2">@_scriptError</div>
|
||||
}
|
||||
else if (_scriptSaved)
|
||||
{
|
||||
<div class="text-success small mt-2">Script saved.</div>
|
||||
}
|
||||
<div class="mt-2">
|
||||
<button type="button" class="btn btn-outline-primary btn-sm"
|
||||
@onclick="SaveScriptAsync" disabled="@_scriptSaving">
|
||||
@if (_scriptSaving) { <span class="spinner-border spinner-border-sm me-1"></span> }
|
||||
Save script
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label class="form-label">Change-triggered</label>
|
||||
@@ -92,8 +135,8 @@
|
||||
}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" @onclick="CancelAsync" disabled="@_busy">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" disabled="@_busy">
|
||||
<button type="button" class="btn btn-outline-secondary" @onclick="CancelAsync" disabled="@(_busy || _scriptSaving)">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" disabled="@(_busy || _scriptSaving)">
|
||||
@if (_busy) { <span class="spinner-border spinner-border-sm me-1"></span> }
|
||||
@(IsNew ? "Create" : "Save changes")
|
||||
</button>
|
||||
@@ -135,8 +178,37 @@
|
||||
private bool _busy;
|
||||
private string? _error;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
// Tracks which open this modal last loaded for, so unrelated Blazor Server re-renders don't
|
||||
// rebuild _form / reload the script source and clobber in-progress edits. Null while closed.
|
||||
private string? _loadedKey;
|
||||
|
||||
// Inline script-source editor state. Independent of _form and the virtual-tag save.
|
||||
private string _scriptSource = "";
|
||||
private bool _scriptLoaded;
|
||||
private byte[] _scriptRowVersion = Array.Empty<byte>();
|
||||
private string? _scriptName;
|
||||
private int _scriptUsageCount;
|
||||
private bool _scriptExpanded;
|
||||
private bool _scriptSaving;
|
||||
private bool _scriptSaved;
|
||||
private string? _scriptError;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!Visible)
|
||||
{
|
||||
_loadedKey = null; // closed → next open reloads fresh
|
||||
return;
|
||||
}
|
||||
|
||||
// Guard against unrelated re-renders. In Blazor Server any live-status push re-invokes
|
||||
// OnParametersSetAsync; without this the rebuild + script reload below would silently
|
||||
// discard whatever the operator has typed. Only rebuild when the modal OPENS or the
|
||||
// target entity CHANGES.
|
||||
var key = IsNew ? "<new>" : Existing?.VirtualTagId;
|
||||
if (key == _loadedKey) return; // same open, re-render → preserve in-progress form + script edits
|
||||
_loadedKey = key;
|
||||
|
||||
// Rebuild the working form whenever the host (re)opens the modal for a fresh target.
|
||||
if (IsNew)
|
||||
{
|
||||
@@ -157,6 +229,105 @@
|
||||
};
|
||||
}
|
||||
_error = null;
|
||||
_scriptExpanded = false;
|
||||
|
||||
// Load the bound script's source for the inline panel when editing an existing tag that
|
||||
// already has a script (the open-load path; selection changes go through OnScriptChangedAsync).
|
||||
await LoadScriptSourceAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads the inline-editor state from the currently selected script. Clears it when no script is
|
||||
/// bound. This drives both the open-load and the post-selection refresh, and never touches _form.
|
||||
/// </summary>
|
||||
private async Task LoadScriptSourceAsync()
|
||||
{
|
||||
_scriptSaved = false;
|
||||
_scriptError = null;
|
||||
|
||||
if (string.IsNullOrEmpty(_form.ScriptId))
|
||||
{
|
||||
_scriptSource = "";
|
||||
_scriptLoaded = false;
|
||||
_scriptRowVersion = Array.Empty<byte>();
|
||||
_scriptName = null;
|
||||
_scriptUsageCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var loaded = await Svc.GetScriptSourceAsync(_form.ScriptId);
|
||||
if (loaded is null)
|
||||
{
|
||||
_scriptSource = "";
|
||||
_scriptLoaded = false;
|
||||
_scriptRowVersion = Array.Empty<byte>();
|
||||
_scriptName = null;
|
||||
_scriptUsageCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_scriptSource = loaded.Value.SourceCode;
|
||||
_scriptLoaded = true;
|
||||
_scriptRowVersion = loaded.Value.RowVersion;
|
||||
_scriptName = loaded.Value.Name;
|
||||
_scriptUsageCount = await Svc.CountVirtualTagsUsingScriptAsync(_form.ScriptId);
|
||||
}
|
||||
|
||||
/// <summary>Refreshes the inline script panel when the operator picks a different script.</summary>
|
||||
private Task OnScriptChangedAsync() => LoadScriptSourceAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the inline script-source panel. Clears the stale "Script saved." banner when
|
||||
/// (re)expanding so a collapse/expand cycle doesn't re-show a confirmation from an earlier save.
|
||||
/// </summary>
|
||||
private void ToggleScriptPanel()
|
||||
{
|
||||
_scriptExpanded = !_scriptExpanded;
|
||||
if (_scriptExpanded)
|
||||
{
|
||||
_scriptSaved = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the edited script body via its own RowVersion-guarded service call. This is wholly
|
||||
/// separate from the virtual-tag save: it does not touch _form, does not raise OnSaved, and does
|
||||
/// not close the modal. On success it re-fetches the source so the panel holds a fresh token.
|
||||
/// </summary>
|
||||
private async Task SaveScriptAsync()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_form.ScriptId) || !_scriptLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_scriptSaving = true;
|
||||
_scriptSaved = false;
|
||||
_scriptError = null;
|
||||
try
|
||||
{
|
||||
var result = await Svc.UpdateScriptSourceAsync(_form.ScriptId, _scriptSource, _scriptRowVersion);
|
||||
if (result.Ok)
|
||||
{
|
||||
// Re-fetch to refresh the concurrency token for any subsequent save.
|
||||
var reloaded = await Svc.GetScriptSourceAsync(_form.ScriptId);
|
||||
if (reloaded is not null)
|
||||
{
|
||||
_scriptSource = reloaded.Value.SourceCode;
|
||||
_scriptRowVersion = reloaded.Value.RowVersion;
|
||||
_scriptName = reloaded.Value.Name;
|
||||
}
|
||||
_scriptSaved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_scriptError = result.Error;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_scriptSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveAsync()
|
||||
|
||||
@@ -47,6 +47,10 @@ public static class EndpointRouteBuilderExtensions
|
||||
services.AddSingleton<IDriverBrowser, OpcUaClientDriverBrowser>();
|
||||
services.AddSingleton<IDriverBrowser, GalaxyDriverBrowser>();
|
||||
|
||||
// Roslyn-backed Monaco script-editor analysis (diagnostics/completions/hover/...).
|
||||
services.AddScoped<ScriptAnalysis.ScriptAnalysisService>();
|
||||
services.AddScoped<ScriptAnalysis.IScriptTagCatalog, ScriptAnalysis.ScriptTagCatalog>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Lists the configured tag + virtual-tag path strings a virtual-tag script author can pass to
|
||||
/// <c>ctx.GetTag("…")</c> / <c>ctx.SetVirtualTag("…")</c>. A later Monaco task uses this to
|
||||
/// autocomplete those string literals.
|
||||
/// </summary>
|
||||
public interface IScriptTagCatalog
|
||||
{
|
||||
/// <summary>Distinct configured tag + virtual-tag paths (the strings a script passes to
|
||||
/// ctx.GetTag/SetVirtualTag), optionally filtered by a literal prefix.</summary>
|
||||
/// <param name="filter">Case-insensitive StartsWith prefix; <c>null</c>/empty returns all (bounded).</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
Task<IReadOnlyList<string>> GetPathsAsync(string? filter, CancellationToken ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default <see cref="IScriptTagCatalog"/>. Returns ONLY the resolvable keys a script may pass to
|
||||
/// <c>ctx.GetTag</c> / <c>ctx.SetVirtualTag</c> — the driver <c>FullName</c> for equipment tags,
|
||||
/// the MXAccess dot-ref for SystemPlatform tags, and the leaf <c>Name</c> (best-effort) for
|
||||
/// virtual tags.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Fidelity over breadth.</b> Verified: the live runtime resolves a <c>ctx.GetTag("X")</c>
|
||||
/// literal against the driver <c>FullName</c> — the resolution chain is
|
||||
/// <c>Phase7Composer.ExtractDependencyRefs</c> harvesting the <c>ctx.GetTag("…")</c> literals
|
||||
/// into <c>EquipmentVirtualTagPlan.DependencyRefs</c>
|
||||
/// (<c>src/Server/…OpcUaServer/Phase7Composer.cs</c>); those become
|
||||
/// <c>VirtualTagActor._dependencyRefs</c>, registered with the
|
||||
/// <c>DependencyMuxActor</c>, whose <c>_byRef</c> map is keyed by
|
||||
/// <c>DriverInstanceActor.AttributeValuePublished.FullReference</c>
|
||||
/// (<c>src/Server/…Runtime/VirtualTags/DependencyMuxActor.cs:97</c>) — and that
|
||||
/// <c>FullReference</c> is the <c>FullName</c> field extracted from <c>Tag.TagConfig</c>
|
||||
/// (see <c>Phase7Composer.ExtractTagFullName</c> + <c>EquipmentNodeWalker.ExtractFullName</c>).
|
||||
/// The UNS-path engine (<c>Core.VirtualTags.VirtualTagEngine</c>, keyed by a slash-joined
|
||||
/// <c>Enterprise/Site/Area/Line/Equipment/TagName</c>) is dormant — it is NOT wired into the
|
||||
/// host — so UNS browse paths never resolve at runtime and are intentionally NOT suggested.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The per-category resolvable key:
|
||||
/// <list type="bullet">
|
||||
/// <item>Equipment driver tag (<c>EquipmentId != null</c>) → the driver <c>FullName</c>
|
||||
/// extracted from <c>Tag.TagConfig</c> (the verified <c>DependencyMux</c> key).</item>
|
||||
/// <item>SystemPlatform tag (<c>EquipmentId == null</c>) → the MXAccess dot-ref
|
||||
/// (<c>FolderPath.Name</c> when a folder is set, else <c>Name</c>) — see
|
||||
/// <c>Phase7Composer</c>'s <c>GalaxyTagPlan.MxAccessRef</c>.</item>
|
||||
/// <item>VirtualTag → its leaf <c>Name</c> only, as a BEST-EFFORT key (the live resolution
|
||||
/// of virtual-tag cascade/write targets is unconfirmed).</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Follow-up: surface the UNS browse path as a completion <i>detail</i> (a non-inserted hint
|
||||
/// shown alongside the resolvable key) for discoverability, rather than as an inserted value.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Each call creates and disposes its own context via the pooled factory — the same pattern
|
||||
/// <c>UnsTreeService</c> uses — so the service is safe to register Scoped per Blazor circuit.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class ScriptTagCatalog(IDbContextFactory<OtOpcUaConfigDbContext> dbFactory) : IScriptTagCatalog
|
||||
{
|
||||
/// <summary>Upper bound on returned suggestions — keeps the completion list responsive on large fleets.</summary>
|
||||
private const int MaxResults = 200;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<string>> GetPathsAsync(string? filter, CancellationToken ct)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
// Only the resolvable keys are projected, so no UNS join is needed: the equipment-tag key is
|
||||
// the FullName from TagConfig, the SystemPlatform key is FolderPath/Name, and the virtual-tag
|
||||
// key is its own Name. Pull just those columns, untracked.
|
||||
var tagRows = await db.Tags
|
||||
.AsNoTracking()
|
||||
.Select(t => new { t.EquipmentId, t.Name, t.FolderPath, t.TagConfig })
|
||||
.ToListAsync(ct);
|
||||
|
||||
var vtagRows = await db.VirtualTags
|
||||
.AsNoTracking()
|
||||
.Select(v => new { v.Name })
|
||||
.ToListAsync(ct);
|
||||
|
||||
var paths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var t in tagRows)
|
||||
{
|
||||
if (t.EquipmentId is null)
|
||||
{
|
||||
// SystemPlatform tag — the Galaxy driver subscribes by the MXAccess dot-ref, which is
|
||||
// "FolderPath.Name" when a folder is set, else just "Name".
|
||||
paths.Add(string.IsNullOrWhiteSpace(t.FolderPath) ? t.Name : $"{t.FolderPath}.{t.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Equipment driver tag — the runtime GetTag key is the driver FullName from TagConfig.
|
||||
paths.Add(ExtractFullNameFromTagConfig(t.TagConfig));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var v in vtagRows)
|
||||
{
|
||||
// Virtual tag — best-effort: the live resolution of virtual-tag cascade/write targets is
|
||||
// unconfirmed, so emit the leaf Name only (no UNS browse path, which never resolves).
|
||||
paths.Add(v.Name);
|
||||
}
|
||||
|
||||
IEnumerable<string> query = paths;
|
||||
if (!string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
query = query.Where(p => p.StartsWith(filter, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return query
|
||||
.OrderBy(p => p, StringComparer.OrdinalIgnoreCase)
|
||||
.Take(MaxResults)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the driver-side full reference from a <c>Tag.TagConfig</c> JSON blob — the
|
||||
/// top-level <c>FullName</c> string every shipped driver stores. Mirrors
|
||||
/// <c>EquipmentNodeWalker.ExtractFullName</c> / <c>Phase7Composer.ExtractTagFullName</c>
|
||||
/// (AdminUI does not reference those assemblies). Falls back to the raw blob when it is not
|
||||
/// a JSON object carrying a string <c>FullName</c>.
|
||||
/// </summary>
|
||||
private static string ExtractFullNameFromTagConfig(string tagConfig)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(tagConfig)) return tagConfig;
|
||||
try
|
||||
{
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(tagConfig);
|
||||
if (doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Object
|
||||
&& doc.RootElement.TryGetProperty("FullName", out var fullName)
|
||||
&& fullName.ValueKind == System.Text.Json.JsonValueKind.String)
|
||||
{
|
||||
return fullName.GetString() ?? tagConfig;
|
||||
}
|
||||
}
|
||||
catch (System.Text.Json.JsonException) { /* fall through to raw blob */ }
|
||||
return tagConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
public record DiagnoseRequest(string Code);
|
||||
public record DiagnoseResponse(IReadOnlyList<DiagnosticMarker> Markers);
|
||||
public record DiagnosticMarker(int Severity, int StartLineNumber, int StartColumn,
|
||||
int EndLineNumber, int EndColumn, string Message, string Code);
|
||||
|
||||
public record CompletionsRequest(string CodeText, int Line, int Column);
|
||||
public record CompletionsResponse(IReadOnlyList<CompletionItem> Items);
|
||||
public record CompletionItem(string Label, string InsertText, string Detail, string Kind, int InsertTextRules = 0);
|
||||
|
||||
public record HoverRequest(string CodeText, int Line, int Column);
|
||||
public record HoverResponse(string? Markdown);
|
||||
|
||||
public record SignatureHelpRequest(string CodeText, int Line, int Column);
|
||||
public record SignatureHelpResponse(string? Label, IReadOnlyList<SignatureHelpParameter>? Parameters, int ActiveParameter);
|
||||
public record SignatureHelpParameter(string Label, string? Documentation);
|
||||
|
||||
public record FormatRequest(string Code);
|
||||
public record FormatResponse(string Code);
|
||||
|
||||
public record InlayHintsRequest(string Code);
|
||||
public record InlayHintsResponse(IReadOnlyList<InlayHint> Hints);
|
||||
public record InlayHint(int Line, int Column, string Label);
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
public static class ScriptAnalysisEndpoints
|
||||
{
|
||||
public static IEndpointRouteBuilder MapScriptAnalysisEndpoints(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
var group = endpoints.MapGroup("/api/script-analysis").RequireAuthorization("FleetAdmin");
|
||||
group.MapPost("/diagnostics", (DiagnoseRequest r, ScriptAnalysisService s) => Results.Ok(s.Diagnose(r)));
|
||||
group.MapPost("/completions", async (CompletionsRequest r, ScriptAnalysisService s) => Results.Ok(await s.CompleteAsync(r)));
|
||||
group.MapPost("/hover", (HoverRequest r, ScriptAnalysisService s) => Results.Ok(s.Hover(r)));
|
||||
group.MapPost("/signature-help", (SignatureHelpRequest r, ScriptAnalysisService s) => Results.Ok(s.SignatureHelp(r)));
|
||||
group.MapPost("/format", (FormatRequest r, ScriptAnalysisService s) => Results.Ok(s.Format(r)));
|
||||
group.MapPost("/inlay-hints", (InlayHintsRequest r, ScriptAnalysisService s) => Results.Ok(s.InlayHints(r)));
|
||||
return endpoints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.VirtualTags;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// Roslyn-backed analysis for the Monaco virtual-tag script editor. Re-seats the
|
||||
/// scadabridge analysis surface on OtOpcUa's REAL evaluator wrapper: the user's
|
||||
/// source is wrapped in the same synthesized <c>CompiledScript.Run(globals)</c> shape
|
||||
/// that <see cref="ScriptEvaluator{TContext, TResult}"/> compiles, so diagnostics,
|
||||
/// completions, hover, and signature help all see the exact symbol surface a published
|
||||
/// script gets. Built against the <see cref="ScriptSandbox"/> allow-list for the
|
||||
/// <see cref="VirtualTagContext"/> context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Public methods are placeholders returning empty results — later tasks fill in each
|
||||
/// capability via TDD. The shared <see cref="Analyze"/> seam + the offset helpers are
|
||||
/// the load-bearing part every later capability reuses; keep them even while unused.
|
||||
/// </remarks>
|
||||
public sealed class ScriptAnalysisService
|
||||
{
|
||||
private static readonly SandboxConfig Sandbox = ScriptSandbox.Build(typeof(VirtualTagContext));
|
||||
private static readonly string Preamble = BuildPreamble();
|
||||
private static readonly CSharpCompilationOptions CompileOptions = new(
|
||||
OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release,
|
||||
allowUnsafe: false, warningLevel: 4, nullableContextOptions: NullableContextOptions.Enable);
|
||||
|
||||
private readonly IScriptTagCatalog? _catalog;
|
||||
private readonly ILogger<ScriptAnalysisService>? _logger;
|
||||
public ScriptAnalysisService(IScriptTagCatalog? catalog = null, ILogger<ScriptAnalysisService>? logger = null)
|
||||
{
|
||||
_catalog = catalog;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Mirrors ScriptEvaluator's wrapper EXACTLY (usings from the sandbox + the
|
||||
// Run(ScriptGlobals<VirtualTagContext>) shape), except the analysis return type is
|
||||
// `object` so a shared script stays valid regardless of any virtual tag's DataType.
|
||||
private static string BuildPreamble()
|
||||
{
|
||||
var usings = string.Join("\n", Sandbox.Imports.Select(i => $"using {i};"));
|
||||
var globalsType = $"global::{typeof(ScriptGlobals<>).Namespace}.ScriptGlobals<global::{typeof(VirtualTagContext).FullName}>";
|
||||
return usings + "\n\n"
|
||||
+ "namespace ZB.MOM.WW.OtOpcUa.Core.Scripting.Compiled;\n"
|
||||
+ "public static class CompiledScript\n{\n"
|
||||
+ $" public static object Run({globalsType} globals)\n {{\n"
|
||||
+ " var ctx = globals.ctx;\n#line 1\n";
|
||||
}
|
||||
|
||||
// userSource is appended after Preamble, then the method/class are closed.
|
||||
private static (SyntaxTree Tree, CSharpCompilation Compilation, SemanticModel Model, int PreambleLength) Analyze(string userSource)
|
||||
{
|
||||
var prefix = Preamble;
|
||||
var full = prefix + (userSource ?? "") + "\n }\n}\n";
|
||||
var tree = CSharpSyntaxTree.ParseText(full);
|
||||
var compilation = CSharpCompilation.Create("ScriptAnalysis", new[] { tree }, Sandbox.References, CompileOptions);
|
||||
return (tree, compilation, compilation.GetSemanticModel(tree), prefix.Length);
|
||||
}
|
||||
|
||||
// editor (line,col) in USER-source coords -> physical offset in the wrapped document
|
||||
private static int OffsetInWrapped(string userSource, int line, int column, int preambleLength)
|
||||
=> preambleLength + Math.Clamp(PositionToOffset(userSource ?? "", line, column), 0, (userSource ?? "").Length);
|
||||
|
||||
private static int PositionToOffset(string code, int line, int column)
|
||||
{
|
||||
int offset = 0, curLine = 1, curCol = 1;
|
||||
for (int i = 0; i < code.Length; i++)
|
||||
{
|
||||
if (curLine == line && curCol == column) return offset;
|
||||
if (code[i] == '\n') { curLine++; curCol = 1; } else { curCol++; }
|
||||
offset = i + 1;
|
||||
}
|
||||
return code.Length;
|
||||
}
|
||||
|
||||
private static string Normalize(string? s) => (s ?? "").Replace("\r\n", "\n").Replace("\r", "\n");
|
||||
|
||||
public DiagnoseResponse Diagnose(DiagnoseRequest req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.Code)) return new DiagnoseResponse(Array.Empty<DiagnosticMarker>());
|
||||
try
|
||||
{
|
||||
var code = Normalize(req.Code);
|
||||
var (tree, compilation, _, preambleLength) = Analyze(code);
|
||||
var markers = new List<DiagnosticMarker>();
|
||||
|
||||
// (1) Roslyn — surface ONLY compile errors, the same severity that actually blocks a
|
||||
// publish in ScriptEvaluator.Compile (it filters GetDiagnostics() to
|
||||
// DiagnosticSeverity.Error). Mirroring that here keeps editor markers in lockstep with
|
||||
// what fails publish: a nullable warning like CS8605 on the canonical passthrough shape
|
||||
// `return ctx.GetTag("X").Value;` compiles + publishes fine under the evaluator's
|
||||
// nullable-enabled options, so flagging it in the editor would be misleading noise.
|
||||
// Restricted to diagnostics physically inside the user source — #line 1 in the preamble
|
||||
// makes GetMappedLineSpan report user-source coords (GetLineSpan would return the
|
||||
// physical wrapper line and be wrong); the preambleLength guard also drops phantom
|
||||
// wrapper diagnostics like CS0161 on Run() when the user hasn't typed `return` yet.
|
||||
foreach (var d in compilation.GetDiagnostics())
|
||||
{
|
||||
if (d.Severity != DiagnosticSeverity.Error || !d.Location.IsInSource) continue;
|
||||
if (d.Location.SourceSpan.Start < preambleLength) continue;
|
||||
var span = d.Location.GetMappedLineSpan(); // honors #line 1 → user coords
|
||||
markers.Add(ToMarker(span, Sev(d.Severity), d.GetMessage(), d.Id));
|
||||
}
|
||||
// (2) ForbiddenTypeAnalyzer — spans are in the wrapped tree; map via #line.
|
||||
foreach (var r in ForbiddenTypeAnalyzer.Analyze(compilation))
|
||||
markers.Add(ToMarker(tree.GetMappedLineSpan(r.Span), 8, r.Message, "OTSCRIPT_FORBIDDEN"));
|
||||
// (3) DependencyExtractor — spans are offsets in the normalized user source; map directly.
|
||||
foreach (var r in DependencyExtractor.Extract(code).Rejections)
|
||||
markers.Add(ToUserMarker(code, r.Span, r.Message, "OTSCRIPT_DYNPATH"));
|
||||
|
||||
return new DiagnoseResponse(markers.Distinct().ToList());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, "Script diagnostics failed; returning no markers.");
|
||||
return new DiagnoseResponse(Array.Empty<DiagnosticMarker>());
|
||||
}
|
||||
}
|
||||
|
||||
private static int Sev(DiagnosticSeverity s) => s switch
|
||||
{
|
||||
DiagnosticSeverity.Error => 8,
|
||||
DiagnosticSeverity.Warning => 4,
|
||||
DiagnosticSeverity.Info => 2,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
private static DiagnosticMarker ToMarker(FileLinePositionSpan span, int sev, string msg, string code) => new(
|
||||
sev, span.StartLinePosition.Line + 1, span.StartLinePosition.Character + 1,
|
||||
span.EndLinePosition.Line + 1, span.EndLinePosition.Character + 1, msg, code);
|
||||
|
||||
private static DiagnosticMarker ToUserMarker(string userSource, Microsoft.CodeAnalysis.Text.TextSpan span, string msg, string code)
|
||||
{
|
||||
var (sl, sc) = LineCol(userSource, span.Start);
|
||||
var (el, ec) = LineCol(userSource, span.End);
|
||||
return new DiagnosticMarker(8, sl, sc, el, ec, msg, code);
|
||||
}
|
||||
|
||||
private static (int Line, int Col) LineCol(string code, int offset)
|
||||
{
|
||||
int line = 1, col = 1;
|
||||
for (int i = 0; i < offset && i < code.Length; i++)
|
||||
if (code[i] == '\n') { line++; col = 1; } else col++;
|
||||
return (line, col);
|
||||
}
|
||||
public async Task<CompletionsResponse> CompleteAsync(CompletionsRequest req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.CodeText)) return new CompletionsResponse(Array.Empty<CompletionItem>());
|
||||
try
|
||||
{
|
||||
var code = Normalize(req.CodeText);
|
||||
var (tree, _, model, preambleLength) = Analyze(code);
|
||||
var position = OffsetInWrapped(code, req.Line, req.Column, preambleLength);
|
||||
var root = await tree.GetRootAsync();
|
||||
// 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))
|
||||
{
|
||||
var paths = await _catalog.GetPathsAsync(pathPrefix, CancellationToken.None);
|
||||
return new CompletionsResponse(
|
||||
paths.Select(p => new CompletionItem(p, p, "tag path", "Field")).ToList());
|
||||
}
|
||||
|
||||
var dot = TryGetDotMembers(token, model);
|
||||
if (dot != null) return new CompletionsResponse(dot);
|
||||
|
||||
var scoped = model.LookupSymbols(position)
|
||||
.Where(s => !s.IsImplicitlyDeclared && !string.IsNullOrEmpty(s.Name))
|
||||
.GroupBy(s => s.Name).Select(g => g.First())
|
||||
.Select(ToCompletionItem).Take(200).ToList();
|
||||
return new CompletionsResponse(scoped);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogWarning(ex, "Script completion failed; returning none.");
|
||||
return new CompletionsResponse(Array.Empty<CompletionItem>());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CompletionItem>? TryGetDotMembers(SyntaxToken token, SemanticModel model)
|
||||
{
|
||||
var memberAccess = token.Parent as MemberAccessExpressionSyntax
|
||||
?? token.GetPreviousToken().Parent as MemberAccessExpressionSyntax;
|
||||
if (memberAccess == null) return null;
|
||||
var typeInfo = model.GetTypeInfo(memberAccess.Expression);
|
||||
var type = typeInfo.Type ?? typeInfo.ConvertedType;
|
||||
if (type == null) return null;
|
||||
return type.GetMembers()
|
||||
.Where(m => m.CanBeReferencedByName && !m.IsImplicitlyDeclared)
|
||||
// NotApplicable covers members (e.g. some BCL/special members) that carry no explicit accessibility but are referenceable.
|
||||
.Where(m => m.DeclaredAccessibility == Accessibility.Public || m.DeclaredAccessibility == Accessibility.NotApplicable)
|
||||
.GroupBy(m => m.Name).Select(g => g.First())
|
||||
.Select(ToCompletionItem).Take(200).ToList();
|
||||
}
|
||||
|
||||
private static bool TryGetTagPathLiteral(SyntaxToken token, out string prefix)
|
||||
{
|
||||
prefix = "";
|
||||
var literal = token.Parent as LiteralExpressionSyntax
|
||||
?? token.GetPreviousToken().Parent as LiteralExpressionSyntax;
|
||||
if (literal is null || !literal.IsKind(SyntaxKind.StringLiteralExpression)) return false;
|
||||
if (literal.Parent is not ArgumentSyntax arg) return false;
|
||||
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;
|
||||
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 ?? "";
|
||||
return true;
|
||||
}
|
||||
|
||||
private static CompletionItem ToCompletionItem(ISymbol symbol)
|
||||
{
|
||||
var kind = symbol.Kind switch
|
||||
{
|
||||
SymbolKind.Method => "Method", SymbolKind.Property => "Property", SymbolKind.Field => "Field",
|
||||
SymbolKind.Event => "Event", SymbolKind.NamedType => "Class", SymbolKind.Local => "Variable",
|
||||
SymbolKind.Parameter => "Variable", SymbolKind.Namespace => "Module", _ => "Text"
|
||||
};
|
||||
return new CompletionItem(symbol.Name, symbol.Name,
|
||||
symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), kind);
|
||||
}
|
||||
public HoverResponse Hover(HoverRequest req)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(req.CodeText)) return new HoverResponse(null);
|
||||
try
|
||||
{
|
||||
var code = Normalize(req.CodeText);
|
||||
var (tree, _, model, preambleLength) = Analyze(code);
|
||||
var position = OffsetInWrapped(code, req.Line, req.Column, preambleLength);
|
||||
var token = tree.GetRoot().FindToken(Math.Max(0, position - 1));
|
||||
var node = token.Parent;
|
||||
if (node is null) return new HoverResponse(null);
|
||||
// Resolve the token's node; if that yields nothing, climb to the enclosing
|
||||
// member-access (e.g. hovering the `GetTag` identifier — the IdentifierName itself
|
||||
// carries no symbol info, but its parent member-access binds to the method).
|
||||
var symbol = ResolveSymbol(model, node);
|
||||
if (symbol is null) return new HoverResponse(null);
|
||||
var display = symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
|
||||
var summary = TryGetXmlSummary(symbol);
|
||||
var md = "```csharp\n" + display + "\n```" + (summary is null ? "" : "\n\n" + summary);
|
||||
return new HoverResponse(md);
|
||||
}
|
||||
catch (Exception ex) { _logger?.LogWarning(ex, "Script hover failed."); return new HoverResponse(null); }
|
||||
}
|
||||
|
||||
private static ISymbol? ResolveSymbol(SemanticModel model, SyntaxNode node)
|
||||
{
|
||||
foreach (var candidate in ResolutionCandidates(node))
|
||||
{
|
||||
var info = model.GetSymbolInfo(candidate);
|
||||
var symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
|
||||
if (symbol is not null) return symbol;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// The node under the caret, then — only if it is the name of a member access — that member access,
|
||||
// and then its enclosing invocation. Bounded so a hover never resolves an unrelated ancestor symbol.
|
||||
private static IEnumerable<SyntaxNode> ResolutionCandidates(SyntaxNode node)
|
||||
{
|
||||
yield return node;
|
||||
if (node is IdentifierNameSyntax
|
||||
&& node.Parent is MemberAccessExpressionSyntax ma
|
||||
&& ma.Name == node)
|
||||
{
|
||||
yield return ma;
|
||||
if (ma.Parent is InvocationExpressionSyntax inv && inv.Expression == ma)
|
||||
yield return inv;
|
||||
}
|
||||
}
|
||||
|
||||
// Best-effort: returns the <summary> text if the symbol's XML doc is available, else null.
|
||||
private static string? TryGetXmlSummary(ISymbol symbol)
|
||||
{
|
||||
var xml = symbol.GetDocumentationCommentXml();
|
||||
if (string.IsNullOrWhiteSpace(xml)) return null;
|
||||
try
|
||||
{
|
||||
var doc = System.Xml.Linq.XDocument.Parse(xml);
|
||||
var summary = doc.Descendants("summary").FirstOrDefault()?.Value.Trim();
|
||||
return string.IsNullOrWhiteSpace(summary) ? null : summary;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
public SignatureHelpResponse SignatureHelp(SignatureHelpRequest req)
|
||||
{
|
||||
var empty = new SignatureHelpResponse(null, null, 0);
|
||||
if (string.IsNullOrWhiteSpace(req.CodeText)) return empty;
|
||||
try
|
||||
{
|
||||
var code = Normalize(req.CodeText);
|
||||
var (tree, _, model, preambleLength) = Analyze(code);
|
||||
var position = OffsetInWrapped(code, req.Line, req.Column, preambleLength);
|
||||
var token = tree.GetRoot().FindToken(Math.Max(0, position - 1));
|
||||
|
||||
InvocationExpressionSyntax? inv = null;
|
||||
for (var n = token.Parent; n is not null; n = n.Parent)
|
||||
if (n is InvocationExpressionSyntax found) { inv = found; break; }
|
||||
if (inv is null) return empty;
|
||||
|
||||
var info = model.GetSymbolInfo(inv);
|
||||
var method = (info.Symbol ?? info.CandidateSymbols.FirstOrDefault()) as IMethodSymbol;
|
||||
if (method is null) return empty;
|
||||
|
||||
var ps = method.Parameters
|
||||
.Select(p => new SignatureHelpParameter(
|
||||
$"{p.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)} {p.Name}", null))
|
||||
.ToList();
|
||||
var label = $"{method.Name}(" + string.Join(", ", ps.Select(p => p.Label)) + ")";
|
||||
|
||||
int active = 0;
|
||||
foreach (var a in inv.ArgumentList.Arguments) { if (a.Span.End < position) active++; else break; }
|
||||
active = Math.Clamp(active, 0, Math.Max(0, ps.Count - 1));
|
||||
|
||||
return new SignatureHelpResponse(label, ps, active);
|
||||
}
|
||||
catch (Exception ex) { _logger?.LogWarning(ex, "Script signature help failed."); return empty; }
|
||||
}
|
||||
public FormatResponse Format(FormatRequest req) // Task 8
|
||||
{
|
||||
if (string.IsNullOrEmpty(req.Code)) return new FormatResponse(req.Code);
|
||||
try
|
||||
{
|
||||
var code = Normalize(req.Code);
|
||||
var tree = CSharpSyntaxTree.ParseText(code,
|
||||
new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script));
|
||||
var formatted = tree.GetRoot().NormalizeWhitespace(indentation: " ", eol: "\n").ToFullString();
|
||||
return new FormatResponse(formatted);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new FormatResponse(req.Code);
|
||||
}
|
||||
}
|
||||
public InlayHintsResponse InlayHints(InlayHintsRequest req) => new(Array.Empty<InlayHint>()); // Task 8 (stays empty)
|
||||
}
|
||||
@@ -401,4 +401,36 @@ public interface IUnsTreeService
|
||||
/// <param name="ct">A token to cancel the operation.</param>
|
||||
/// <returns>Success, a concurrency failure, or a delete-failed failure.</returns>
|
||||
Task<UnsMutationResult> DeleteVirtualTagAsync(string virtualTagId, byte[] rowVersion, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Loads a script's editable source for the inline script-source panel in the virtual-tag modal,
|
||||
/// along with the concurrency token the panel must echo back on save and the script's display name.
|
||||
/// Reads untracked. Returns <c>null</c> when the script no longer exists.
|
||||
/// </summary>
|
||||
/// <param name="scriptId">The script whose source to load.</param>
|
||||
/// <param name="ct">A token to cancel the load.</param>
|
||||
/// <returns>The <c>(SourceCode, RowVersion, Name)</c> triple, or <c>null</c> when missing.</returns>
|
||||
Task<(string SourceCode, byte[] RowVersion, string Name)?> GetScriptSourceAsync(string scriptId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Counts how many virtual tags bind the given script, so the inline editor can warn the operator
|
||||
/// that an edit to a shared script affects every virtual tag using it.
|
||||
/// </summary>
|
||||
/// <param name="scriptId">The script to count usages of.</param>
|
||||
/// <param name="ct">A token to cancel the query.</param>
|
||||
/// <returns>The number of virtual tags whose <c>ScriptId</c> matches.</returns>
|
||||
Task<int> CountVirtualTagsUsingScriptAsync(string scriptId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Saves an edited script body from the inline panel: updates <c>SourceCode</c> and recomputes the
|
||||
/// SHA-256 <c>SourceHash</c> (lower-case hex, matching the Script-edit page). This save is separate
|
||||
/// from the virtual-tag save and is guarded by its own last-write-wins optimistic concurrency on
|
||||
/// <see cref="Configuration.Entities.Script.RowVersion"/>.
|
||||
/// </summary>
|
||||
/// <param name="scriptId">The script to update.</param>
|
||||
/// <param name="sourceCode">The new source body.</param>
|
||||
/// <param name="rowVersion">The concurrency token the panel last read.</param>
|
||||
/// <param name="ct">A token to cancel the operation.</param>
|
||||
/// <returns>Success, a missing-row failure, or a concurrency failure.</returns>
|
||||
Task<(bool Ok, string? Error)> UpdateScriptSourceAsync(string scriptId, string sourceCode, byte[] rowVersion, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
@@ -1014,6 +1016,67 @@ public sealed class UnsTreeService(IDbContextFactory<OtOpcUaConfigDbContext> dbF
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<(string SourceCode, byte[] RowVersion, string Name)?> GetScriptSourceAsync(
|
||||
string scriptId,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
var row = await db.Scripts
|
||||
.AsNoTracking()
|
||||
.Where(s => s.ScriptId == scriptId)
|
||||
.Select(s => new { s.SourceCode, s.RowVersion, s.Name })
|
||||
.FirstOrDefaultAsync(ct);
|
||||
|
||||
return row is null ? null : (row.SourceCode, row.RowVersion, row.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> CountVirtualTagsUsingScriptAsync(string scriptId, CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
return await db.VirtualTags.CountAsync(v => v.ScriptId == scriptId, ct);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<(bool Ok, string? Error)> UpdateScriptSourceAsync(
|
||||
string scriptId,
|
||||
string sourceCode,
|
||||
byte[] rowVersion,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
await using var db = await dbFactory.CreateDbContextAsync(ct);
|
||||
|
||||
var entity = await db.Scripts.FirstOrDefaultAsync(s => s.ScriptId == scriptId, ct);
|
||||
if (entity is null)
|
||||
{
|
||||
return (false, "Script not found.");
|
||||
}
|
||||
|
||||
db.Entry(entity).Property(e => e.RowVersion).OriginalValue = rowVersion;
|
||||
|
||||
entity.SourceCode = sourceCode;
|
||||
entity.SourceHash = HashSource(sourceCode);
|
||||
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync(ct);
|
||||
return (true, null);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return (false, "This script was changed by someone else. Reload and try again.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA-256 of a script body as lower-case hex — the same algorithm the Script-edit
|
||||
/// page uses, so a body saved from either surface yields an identical compile-cache key.
|
||||
/// </summary>
|
||||
private static string HashSource(string source) =>
|
||||
Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(source)));
|
||||
|
||||
/// <summary>
|
||||
/// Validates a virtual tag's script binding and trigger configuration (mirrors the DbContext CHECK
|
||||
/// constraints): a script must be chosen, at least one of change-trigger / timer must be set, and a
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
<ProjectReference Include="..\ZB.MOM.WW.OtOpcUa.ControlPlane\ZB.MOM.WW.OtOpcUa.ControlPlane.csproj"/>
|
||||
<ProjectReference Include="..\ZB.MOM.WW.OtOpcUa.Runtime\ZB.MOM.WW.OtOpcUa.Runtime.csproj"/>
|
||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Configuration\ZB.MOM.WW.OtOpcUa.Configuration.csproj"/>
|
||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Scripting\ZB.MOM.WW.OtOpcUa.Core.Scripting.csproj"/>
|
||||
<ProjectReference Include="..\..\Core\ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions\ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
(function () {
|
||||
const VS_BASE = "/_content/ZB.MOM.WW.OtOpcUa.AdminUI/lib/monaco/vs";
|
||||
const editors = {};
|
||||
let readyPromise = null;
|
||||
|
||||
function ensureLoaded() {
|
||||
if (readyPromise) return readyPromise;
|
||||
readyPromise = new Promise(function (resolve, reject) {
|
||||
const loader = document.createElement("script");
|
||||
loader.src = VS_BASE + "/loader.js";
|
||||
loader.onload = function () {
|
||||
require.config({ paths: { vs: VS_BASE } });
|
||||
require(["vs/editor/editor.main"], function () {
|
||||
registerCSharpProviders();
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
loader.onerror = function (e) { reject(e); };
|
||||
document.head.appendChild(loader);
|
||||
});
|
||||
return readyPromise;
|
||||
}
|
||||
|
||||
const KIND_MAP = { Method: 0, Field: 3, Property: 9, Event: 10, Class: 5, Module: 8, Variable: 4, Text: 18 };
|
||||
|
||||
function registerCSharpProviders() {
|
||||
monaco.languages.registerCompletionItemProvider("csharp", {
|
||||
triggerCharacters: [".", "(", "\""],
|
||||
provideCompletionItems: async function (model, position) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/completions", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ codeText: model.getValue(), line: position.lineNumber, column: position.column })
|
||||
});
|
||||
if (!resp.ok) return { suggestions: [] };
|
||||
const data = await resp.json();
|
||||
const word = model.getWordUntilPosition(position);
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber, endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn, endColumn: word.endColumn
|
||||
};
|
||||
return {
|
||||
suggestions: (data.items || []).map(function (it) {
|
||||
return {
|
||||
label: it.label, insertText: it.insertText, detail: it.detail,
|
||||
kind: KIND_MAP[it.kind] != null ? KIND_MAP[it.kind] : 18,
|
||||
insertTextRules: it.insertTextRules || 0,
|
||||
range: range
|
||||
};
|
||||
})
|
||||
};
|
||||
} catch (e) { return { suggestions: [] }; }
|
||||
}
|
||||
});
|
||||
monaco.languages.registerHoverProvider("csharp", {
|
||||
provideHover: async function (model, position) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/hover", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ codeText: model.getValue(), line: position.lineNumber, column: position.column })
|
||||
});
|
||||
if (!resp.ok) return null;
|
||||
const data = await resp.json();
|
||||
if (!data.markdown) return null;
|
||||
return { contents: [{ value: data.markdown }] };
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
});
|
||||
monaco.languages.registerDocumentFormattingEditProvider("csharp", {
|
||||
provideDocumentFormattingEdits: async function (model) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/format", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ code: model.getValue() })
|
||||
});
|
||||
if (!resp.ok) return [];
|
||||
const data = await resp.json();
|
||||
if (typeof data.code !== "string" || data.code === model.getValue()) return [];
|
||||
return [{ range: model.getFullModelRange(), text: data.code }];
|
||||
} catch (e) { return []; }
|
||||
}
|
||||
});
|
||||
monaco.languages.registerInlayHintsProvider("csharp", {
|
||||
provideInlayHints: async function (model) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/inlay-hints", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ code: model.getValue() })
|
||||
});
|
||||
if (!resp.ok) return { hints: [], dispose: function () {} };
|
||||
const data = await resp.json();
|
||||
return {
|
||||
hints: (data.hints || []).map(function (h) {
|
||||
return { position: { lineNumber: h.line, column: h.column }, label: h.label, kind: 2, paddingRight: true };
|
||||
}),
|
||||
dispose: function () {}
|
||||
};
|
||||
} catch (e) { return { hints: [], dispose: function () {} }; }
|
||||
}
|
||||
});
|
||||
monaco.languages.registerSignatureHelpProvider("csharp", {
|
||||
signatureHelpTriggerCharacters: ["(", ","],
|
||||
signatureHelpRetriggerCharacters: [","],
|
||||
provideSignatureHelp: async function (model, position) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/signature-help", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ codeText: model.getValue(), line: position.lineNumber, column: position.column })
|
||||
});
|
||||
if (!resp.ok) return null;
|
||||
const data = await resp.json();
|
||||
if (!data.label) return null;
|
||||
return {
|
||||
value: {
|
||||
signatures: [{
|
||||
label: data.label,
|
||||
parameters: (data.parameters || []).map(function (p) { return { label: p.label, documentation: p.documentation }; })
|
||||
}],
|
||||
activeSignature: 0,
|
||||
activeParameter: data.activeParameter ?? 0
|
||||
},
|
||||
dispose: function () {}
|
||||
};
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchDiagnostics(model) {
|
||||
try {
|
||||
const resp = await fetch("/api/script-analysis/diagnostics", {
|
||||
method: "POST", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ code: model.getValue() })
|
||||
});
|
||||
if (!resp.ok) return [];
|
||||
const data = await resp.json();
|
||||
return data.markers || [];
|
||||
} catch (e) { return []; }
|
||||
}
|
||||
|
||||
async function createEditor(id, host, options, dotNetRef) {
|
||||
await ensureLoaded();
|
||||
if (!host) return;
|
||||
const editor = monaco.editor.create(host, {
|
||||
value: options.value || "", language: options.language || "csharp",
|
||||
theme: "vs", minimap: { enabled: false }, scrollBeyondLastLine: false,
|
||||
automaticLayout: true, fontSize: 13, lineNumbers: "on",
|
||||
renderLineHighlight: "line", readOnly: !!options.readOnly,
|
||||
tabSize: 4, insertSpaces: true, wordWrap: "off", fixedOverflowWidgets: true,
|
||||
// Auto-suggest inside string literals too, so tag-path completion surfaces while typing
|
||||
// inside ctx.GetTag("…") / ctx.SetVirtualTag("…") without requiring an explicit Ctrl+Space.
|
||||
quickSuggestions: { other: true, comments: false, strings: true }
|
||||
});
|
||||
let diagTimer = null;
|
||||
const scheduleDiagnostics = function () {
|
||||
if (diagTimer) clearTimeout(diagTimer);
|
||||
diagTimer = setTimeout(async function () {
|
||||
const model = editor.getModel(); if (!model) return;
|
||||
const markers = await fetchDiagnostics(model);
|
||||
monaco.editor.setModelMarkers(model, "otopcua", markers);
|
||||
dotNetRef.invokeMethodAsync("OnMarkersChanged", markers).catch(function () {});
|
||||
}, 500);
|
||||
};
|
||||
editor.onDidChangeModelContent(function () {
|
||||
dotNetRef.invokeMethodAsync("OnValueChanged", editor.getValue()).catch(function () {});
|
||||
if (options.language === "csharp") scheduleDiagnostics();
|
||||
});
|
||||
editors[id] = { editor: editor, dotNetRef: dotNetRef };
|
||||
if (options.language === "csharp") scheduleDiagnostics();
|
||||
}
|
||||
function setEditorOption(id, name, value) {
|
||||
const e = editors[id]; if (!e) return;
|
||||
// Note: monaco.editor.setTheme is global — it re-themes every editor on the page, not just this id.
|
||||
if (name === "theme") { monaco.editor.setTheme(value); return; }
|
||||
const u = {}; u[name] = value; e.editor.updateOptions(u);
|
||||
}
|
||||
function format(id) { editors[id]?.editor.getAction("editor.action.formatDocument")?.run(); }
|
||||
function revealLine(id, line, col) {
|
||||
const e = editors[id]; if (!e) return;
|
||||
e.editor.revealLineInCenter(line); e.editor.setPosition({ lineNumber: line, column: col || 1 }); e.editor.focus();
|
||||
}
|
||||
function setValue(id, v) { const e = editors[id]; if (e && e.editor.getValue() !== v) e.editor.setValue(v || ""); }
|
||||
function getValue(id) { const e = editors[id]; return e ? e.editor.getValue() : null; }
|
||||
function setMarkers(id, m) { const e = editors[id]; const model = e && e.editor.getModel(); if (model) monaco.editor.setModelMarkers(model, "otopcua", m || []); }
|
||||
function dispose(id) { const e = editors[id]; if (!e) return; try { e.editor.dispose(); } catch (x) {} delete editors[id]; }
|
||||
|
||||
window.MonacoBlazor = { createEditor, setValue, getValue, setMarkers, setEditorOption, format, revealLine, dispose };
|
||||
})();
|
||||
@@ -1,59 +0,0 @@
|
||||
// Phase 7 Stream F — Monaco editor loader for ScriptEditor.razor.
|
||||
// Progressive enhancement: the textarea is authoritative until Monaco attaches;
|
||||
// after attach, Monaco syncs back into the textarea on every change so Blazor's
|
||||
// @bind still sees the latest value.
|
||||
|
||||
(function () {
|
||||
if (window.otOpcUaScriptEditor) return;
|
||||
|
||||
const MONACO_CDN = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs';
|
||||
let loaderPromise = null;
|
||||
|
||||
function ensureLoader() {
|
||||
if (loaderPromise) return loaderPromise;
|
||||
loaderPromise = new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script');
|
||||
script.src = `${MONACO_CDN}/loader.js`;
|
||||
script.onload = () => {
|
||||
window.require.config({ paths: { vs: MONACO_CDN } });
|
||||
window.require(['vs/editor/editor.main'], () => resolve(window.monaco));
|
||||
};
|
||||
script.onerror = () => reject(new Error('Monaco CDN unreachable'));
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
return loaderPromise;
|
||||
}
|
||||
|
||||
window.otOpcUaScriptEditor = {
|
||||
attach: async function (textareaId) {
|
||||
const ta = document.getElementById(textareaId);
|
||||
if (!ta) return;
|
||||
const monaco = await ensureLoader();
|
||||
|
||||
// Mount Monaco over the textarea. The textarea stays in the DOM as the
|
||||
// source of truth for Blazor's @bind — Monaco mirrors into it on every
|
||||
// keystroke so server-side state stays in sync.
|
||||
const host = document.createElement('div');
|
||||
host.style.height = '340px';
|
||||
host.style.border = '1px solid #ced4da';
|
||||
host.style.borderRadius = '0.25rem';
|
||||
ta.style.display = 'none';
|
||||
ta.parentNode.insertBefore(host, ta);
|
||||
|
||||
const editor = monaco.editor.create(host, {
|
||||
value: ta.value,
|
||||
language: 'csharp',
|
||||
theme: 'vs',
|
||||
automaticLayout: true,
|
||||
fontSize: 13,
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
});
|
||||
|
||||
editor.onDidChangeModelContent(() => {
|
||||
ta.value = editor.getValue();
|
||||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
});
|
||||
},
|
||||
};
|
||||
})();
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/_commonjsHelpers-CT9FvmAN",["exports"],(function(t){"use strict";function o(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}t.getDefaultExportFromCjs=o}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/apex-CcIm7xu6",["exports"],(function(s){"use strict";const o={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:<editor-fold\\b))"),end:new RegExp("^\\s*//\\s*(?:(?:#?endregion\\b)|(?:</editor-fold>))")}}},n=["abstract","activate","and","any","array","as","asc","assert","autonomous","begin","bigdecimal","blob","boolean","break","bulk","by","case","cast","catch","char","class","collect","commit","const","continue","convertcurrency","decimal","default","delete","desc","do","double","else","end","enum","exception","exit","export","extends","false","final","finally","float","for","from","future","get","global","goto","group","having","hint","if","implements","import","in","inner","insert","instanceof","int","interface","into","join","last_90_days","last_month","last_n_days","last_week","like","limit","list","long","loop","map","merge","native","new","next_90_days","next_month","next_n_days","next_week","not","null","nulls","number","object","of","on","or","outer","override","package","parallel","pragma","private","protected","public","retrieve","return","returning","rollback","savepoint","search","select","set","short","sort","stat","static","strictfp","super","switch","synchronized","system","testmethod","then","this","this_month","this_week","throw","throws","today","tolabel","tomorrow","transaction","transient","trigger","true","try","type","undelete","update","upsert","using","virtual","void","volatile","webservice","when","where","while","yesterday"],i=e=>e.charAt(0).toUpperCase()+e.substr(1);let t=[];n.forEach(e=>{t.push(e),t.push(e.toUpperCase()),t.push(i(e))});const a={defaultToken:"",tokenPostfix:".apex",keywords:t,operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,tokenizer:{root:[[/[a-z_$][\w$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],[/[A-Z][\w\$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"type.identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@apexdoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],apexdoc:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}};s.conf=o,s.language=a,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|
||||
+93
File diff suppressed because one or more lines are too long
+26
File diff suppressed because one or more lines are too long
+470
File diff suppressed because one or more lines are too long
+58
File diff suppressed because one or more lines are too long
+67731
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/azcli-BA0tQDCg",["exports"],(function(e){"use strict";const t={comments:{lineComment:"#"}},n={defaultToken:"keyword",ignoreCase:!0,tokenPostfix:".azcli",str:/[^#\s]/,tokenizer:{root:[{include:"@comment"},[/\s-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}],[/^-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}]],type:[{include:"@comment"},[/-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":"key.identifier"}}],[/@str+\s*/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}]],comment:[[/#.*$/,{cases:{"@eos":{token:"comment",next:"@popall"}}}]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/bat-C397hTD6",["exports"],(function(e){"use strict";const o={comments:{lineComment:"REM"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],folding:{markers:{start:new RegExp("^\\s*(::\\s*|REM\\s+)#region"),end:new RegExp("^\\s*(::\\s*|REM\\s+)#endregion")}}},s={defaultToken:"",ignoreCase:!0,tokenPostfix:".bat",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"}],keywords:/call|defined|echo|errorlevel|exist|for|goto|if|pause|set|shift|start|title|not|pushd|popd/,symbols:/[=><!~?&|+\-*\/\^;\.,]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^(\s*)(rem(?:\s.*|))$/,["","comment"]],[/(\@?)(@keywords)(?!\w)/,[{token:"keyword"},{token:"keyword.$2"}]],[/[ \t\r\n]+/,""],[/setlocal(?!\w)/,"keyword.tag-setlocal"],[/endlocal(?!\w)/,"keyword.tag-setlocal"],[/[a-zA-Z_]\w*/,""],[/:\w*/,"metatag"],[/%[^%]+%/,"variable"],[/%%[\w]+(?!\w)/,"variable"],[/[{}()\[\]]/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F_]*[0-9a-fA-F]/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],string:[[/[^\\"'%]+/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/%[\w ]+%/,"variable"],[/%%[\w]+(?!\w)/,"variable"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/$/,"string","@popall"]]}};e.conf=o,e.language=s,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1,2 @@
|
||||
define("vs/bicep-DF5aW17k",["exports"],(function(e){"use strict";const n=(a=>`\\b${a}\\b`)("[_a-zA-Z][_a-zA-Z0-9]*"),t=["targetScope","resource","module","param","var","output","for","in","if","existing"],o=["true","false","null"],i="[ \\t\\r\\n]",r="[0-9]+",c={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'"},{open:"'''",close:"'''"}],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:"'''",close:"'''",notIn:["string","comment"]}],autoCloseBefore:`:.,=}])'
|
||||
`,indentationRules:{increaseIndentPattern:new RegExp("^((?!\\/\\/).)*(\\{[^}\"'`]*|\\([^)\"'`]*|\\[[^\\]\"'`]*)$"),decreaseIndentPattern:new RegExp("^((?!.*?\\/\\*).*\\*/)?\\s*[\\}\\]].*$")}},s={defaultToken:"",tokenPostfix:".bicep",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],symbols:/[=><!~?:&|+\-*/^%]+/,keywords:t,namedLiterals:o,escapes:"\\\\(u{[0-9A-Fa-f]+}|n|r|t|\\\\|'|\\${)",tokenizer:{root:[{include:"@expression"},{include:"@whitespace"}],stringVerbatim:[{regex:"(|'|'')[^']",action:{token:"string"}},{regex:"'''",action:{token:"string.quote",next:"@pop"}}],stringLiteral:[{regex:"\\${",action:{token:"delimiter.bracket",next:"@bracketCounting"}},{regex:"[^\\\\'$]+",action:{token:"string"}},{regex:"@escapes",action:{token:"string.escape"}},{regex:"\\\\.",action:{token:"string.escape.invalid"}},{regex:"'",action:{token:"string",next:"@pop"}}],bracketCounting:[{regex:"{",action:{token:"delimiter.bracket",next:"@bracketCounting"}},{regex:"}",action:{token:"delimiter.bracket",next:"@pop"}},{include:"expression"}],comment:[{regex:"[^\\*]+",action:{token:"comment"}},{regex:"\\*\\/",action:{token:"comment",next:"@pop"}},{regex:"[\\/*]",action:{token:"comment"}}],whitespace:[{regex:i},{regex:"\\/\\*",action:{token:"comment",next:"@comment"}},{regex:"\\/\\/.*$",action:{token:"comment"}}],expression:[{regex:"'''",action:{token:"string.quote",next:"@stringVerbatim"}},{regex:"'",action:{token:"string.quote",next:"@stringLiteral"}},{regex:r,action:{token:"number"}},{regex:n,action:{cases:{"@keywords":{token:"keyword"},"@namedLiterals":{token:"keyword"},"@default":{token:"identifier"}}}}]}};e.conf=c,e.language=s,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/cameligo-plsz8qhj",["exports"],(function(e){"use strict";const o={comments:{lineComment:"//",blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"],["<",">"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'},{open:"(*",close:"*)"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'},{open:"(*",close:"*)"}]},t={defaultToken:"",tokenPostfix:".cameligo",ignoreCase:!0,brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],keywords:["abs","assert","block","Bytes","case","Crypto","Current","else","failwith","false","for","fun","if","in","let","let%entry","let%init","List","list","Map","map","match","match%nat","mod","not","operation","Operation","of","record","Set","set","sender","skip","source","String","then","to","true","type","with"],typeKeywords:["int","unit","string","tz","nat","bool"],operators:["=",">","<","<=",">=","<>",":",":=","and","mod","or","+","-","*","/","@","&","^","%","->","<-","&&","||"],symbols:/[=><:@\^&|+\-*\/\^%]+/,tokenizer:{root:[[/[a-zA-Z_][\w]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/\$[0-9a-fA-F]{1,16}/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'/,"string","@string"],[/'[^\\']'/,"string"],[/'/,"string.invalid"],[/\#\d+/,"string"]],comment:[[/[^\(\*]+/,"comment"],[/\*\)/,"comment","@pop"],[/\(\*/,"comment"]],string:[[/[^\\']+/,"string"],[/\\./,"string.escape.invalid"],[/'/,{token:"string.quote",bracket:"@close",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,"white"],[/\(\*/,"comment","@comment"],[/\/\/.*$/,"comment"]]}};e.conf=o,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/coffee-Bu45yuWE",["exports"],(function(e){"use strict";const n={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#%\^\&\*\(\)\=\$\-\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{blockComment:["###","###"],lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*#region\\b"),end:new RegExp("^\\s*#endregion\\b")}}},t={defaultToken:"",ignoreCase:!0,tokenPostfix:".coffee",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],regEx:/\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,keywords:["and","or","is","isnt","not","on","yes","@","no","off","true","false","null","this","new","delete","typeof","in","instanceof","return","throw","break","continue","debugger","if","else","switch","for","while","do","try","catch","finally","class","extends","super","undefined","then","unless","until","loop","of","by","when"],symbols:/[=><!~?&%|+\-*\/\^\.,\:]+/,escapes:/\\(?:[abfnrtv\\"'$]|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/\@[a-zA-Z_]\w*/,"variable.predefined"],[/[a-zA-Z_]\w*/,{cases:{this:"variable.predefined","@keywords":{token:"keyword.$0"},"@default":""}}],[/[ \t\r\n]+/,""],[/###/,"comment","@comment"],[/#.*$/,"comment"],["///",{token:"regexp",next:"@hereregexp"}],[/^(\s*)(@regEx)/,["","regexp"]],[/(\()(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\,)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\=)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\:)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\[)(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\!)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\&)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\|)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\?)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\{)(\s*)(@regEx)/,["@brackets","","regexp"]],[/(\;)(\s*)(@regEx)/,["","","regexp"]],[/}/,{cases:{"$S2==interpolatedstring":{token:"string",next:"@pop"},"@default":"@brackets"}}],[/[{}()\[\]]/,"@brackets"],[/@symbols/,"delimiter"],[/\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d+\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/0[0-7]+(?!\d)/,"number.octal"],[/\d+/,"number"],[/[,.]/,"delimiter"],[/"""/,"string",'@herestring."""'],[/'''/,"string","@herestring.'''"],[/"/,{cases:{"@eos":"string","@default":{token:"string",next:'@string."'}}}],[/'/,{cases:{"@eos":"string","@default":{token:"string",next:"@string.'"}}}]],string:[[/[^"'\#\\]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/\./,"string.escape.invalid"],[/#{/,{cases:{'$S2=="':{token:"string",next:"root.interpolatedstring"},"@default":"string"}}],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/#/,"string"]],herestring:[[/("""|''')/,{cases:{"$1==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/[^#\\'"]+/,"string"],[/['"]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/#{/,{token:"string.quote",next:"root.interpolatedstring"}],[/#/,"string"]],comment:[[/[^#]+/,"comment"],[/###/,"comment","@pop"],[/#/,"comment"]],hereregexp:[[/[^\\\/#]+/,"regexp"],[/\\./,"regexp"],[/#.*$/,"comment"],["///[igm]*",{token:"regexp",next:"@pop"}],[/\//,"regexp"]]}};e.conf=n,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/csharp-CX28MZyh",["exports"],(function(e){"use strict";const t={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],folding:{markers:{start:new RegExp("^\\s*#region\\b"),end:new RegExp("^\\s*#endregion\\b")}}},n={defaultToken:"",tokenPostfix:".cs",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],keywords:["extern","alias","using","bool","decimal","sbyte","byte","short","ushort","int","uint","long","ulong","char","float","double","object","dynamic","string","assembly","is","as","ref","out","this","base","new","typeof","void","checked","unchecked","default","delegate","var","const","if","else","switch","case","while","do","for","foreach","in","break","continue","goto","return","throw","try","catch","finally","lock","yield","from","let","where","join","on","equals","into","orderby","ascending","descending","select","group","by","namespace","partial","class","field","event","method","param","public","protected","internal","private","abstract","sealed","static","struct","readonly","volatile","virtual","override","params","get","set","add","remove","operator","true","false","implicit","explicit","interface","enum","null","async","await","fixed","sizeof","stackalloc","unsafe","nameof","when"],namespaceFollows:["namespace","using"],parenFollows:["if","for","while","switch","foreach","using","catch","when"],operators:["=","??","||","&&","|","^","&","==","!=","<=",">=","<<","+","-","*","/","%","!","~","++","--","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=",">>","=>"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/\@?[a-zA-Z_]\w*/,{cases:{"@namespaceFollows":{token:"keyword.$0",next:"@namespace"},"@keywords":{token:"keyword.$0",next:"@qualified"},"@default":{token:"identifier",next:"@qualified"}}}],{include:"@whitespace"},[/}/,{cases:{"$S2==interpolatedstring":{token:"string.quote",next:"@pop"},"$S2==litinterpstring":{token:"string.quote",next:"@pop"},"@default":"@brackets"}}],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/,"number.float"],[/0[xX][0-9a-fA-F_]+/,"number.hex"],[/0[bB][01_]+/,"number.hex"],[/[0-9_]+/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,{token:"string.quote",next:"@string"}],[/\$\@"/,{token:"string.quote",next:"@litinterpstring"}],[/\@"/,{token:"string.quote",next:"@litstring"}],[/\$"/,{token:"string.quote",next:"@interpolatedstring"}],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],qualified:[[/[a-zA-Z_][\w]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],[/\./,"delimiter"],["","","@pop"]],namespace:[{include:"@whitespace"},[/[A-Z]\w*/,"namespace"],[/[\.=]/,"delimiter"],["","","@pop"]],comment:[[/[^\/*]+/,"comment"],["\\*/","comment","@pop"],[/[\/*]/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",next:"@pop"}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]],litinterpstring:[[/[^"{]+/,"string"],[/""/,"string.escape"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.litinterpstring"}],[/"/,{token:"string.quote",next:"@pop"}]],interpolatedstring:[[/[^\\"{]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.interpolatedstring"}],[/"/,{token:"string.quote",next:"@pop"}]],whitespace:[[/^[ \t\v\f]*#((r)|(load))(?=\s)/,"directive.csx"],[/^[ \t\v\f]*#\w.*$/,"namespace.cpp"],[/[ \t\v\f\r\n]+/,""],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/csp-D8uWnyxW",["exports"],(function(t){"use strict";const e={brackets:[],autoClosingPairs:[],surroundingPairs:[]},r={keywords:[],typeKeywords:[],tokenPostfix:".csp",operators:[],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/child-src/,"string.quote"],[/connect-src/,"string.quote"],[/default-src/,"string.quote"],[/font-src/,"string.quote"],[/frame-src/,"string.quote"],[/img-src/,"string.quote"],[/manifest-src/,"string.quote"],[/media-src/,"string.quote"],[/object-src/,"string.quote"],[/script-src/,"string.quote"],[/style-src/,"string.quote"],[/worker-src/,"string.quote"],[/base-uri/,"string.quote"],[/plugin-types/,"string.quote"],[/sandbox/,"string.quote"],[/disown-opener/,"string.quote"],[/form-action/,"string.quote"],[/frame-ancestors/,"string.quote"],[/report-uri/,"string.quote"],[/report-to/,"string.quote"],[/upgrade-insecure-requests/,"string.quote"],[/block-all-mixed-content/,"string.quote"],[/require-sri-for/,"string.quote"],[/reflected-xss/,"string.quote"],[/referrer/,"string.quote"],[/policy-uri/,"string.quote"],[/'self'/,"string.quote"],[/'unsafe-inline'/,"string.quote"],[/'unsafe-eval'/,"string.quote"],[/'strict-dynamic'/,"string.quote"],[/'unsafe-hashed-attributes'/,"string.quote"]]}};t.conf=e,t.language=r,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1,3 @@
|
||||
define("vs/css-CaeNmE3S",["exports"],(function(e){"use strict";const t={wordPattern:/(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,comments:{blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*\\/\\*\\s*#region\\b\\s*(.*?)\\s*\\*\\/"),end:new RegExp("^\\s*\\/\\*\\s*#endregion\\b.*\\*\\/")}}},n={defaultToken:"",tokenPostfix:".css",ws:`[
|
||||
\r\f]*`,identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.bracket"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@selector"}],selector:[{include:"@comments"},{include:"@import"},{include:"@strings"},["[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)",{token:"keyword",next:"@keyframedeclaration"}],["[@](page|content|font-face|-moz-document)",{token:"keyword"}],["[@](charset|namespace)",{token:"keyword",next:"@declarationbody"}],["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@selectorname"},["[\\*]","tag"],["[>\\+,]","delimiter"],["\\[",{token:"delimiter.bracket",next:"@selectorattribute"}],["{",{token:"delimiter.bracket",next:"@selectorbody"}]],selectorbody:[{include:"@comments"},["[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))","attribute.name","@rulevalue"],["}",{token:"delimiter.bracket",next:"@pop"}]],selectorname:[["(\\.|#(?=[^{])|%|(@identifier)|:)+","tag"]],selectorattribute:[{include:"@term"},["]",{token:"delimiter.bracket",next:"@pop"}]],term:[{include:"@comments"},["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@functioninvocation"},{include:"@numbers"},{include:"@name"},{include:"@strings"},["([<>=\\+\\-\\*\\/\\^\\|\\~,])","delimiter"],[",","delimiter"]],rulevalue:[{include:"@comments"},{include:"@strings"},{include:"@term"},["!important","keyword"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],warndebug:[["[@](warn|debug)",{token:"keyword",next:"@declarationbody"}]],import:[["[@](import)",{token:"keyword",next:"@declarationbody"}]],urldeclaration:[{include:"@strings"},[`[^)\r
|
||||
]+`,"string"],["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],parenthizedterm:[{include:"@term"},["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],declarationbody:[{include:"@term"},[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[/[^*/]+/,"comment"],[/./,"comment"]],name:[["@identifier","attribute.value"]],numbers:[["-?(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|fr|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],keyframedeclaration:[["@identifier","attribute.value"],["{",{token:"delimiter.bracket",switchTo:"@keyframebody"}]],keyframebody:[{include:"@term"},["{",{token:"delimiter.bracket",next:"@selectorbody"}],["}",{token:"delimiter.bracket",next:"@pop"}]],functioninvocation:[["@identifier\\(",{token:"attribute.value",next:"@functionarguments"}]],functionarguments:[["\\$@identifier@ws:","attribute.name"],["[,]","delimiter"],{include:"@term"},["\\)",{token:"attribute.value",next:"@pop"}]],strings:[['~?"',{token:"string",next:"@stringenddoublequote"}],["~?'",{token:"string",next:"@stringendquote"}]],stringenddoublequote:[["\\\\.","string"],['"',{token:"string",next:"@pop"}],[/[^\\"]+/,"string"],[".","string"]],stringendquote:[["\\\\.","string"],["'",{token:"string",next:"@pop"}],[/[^\\']+/,"string"],[".","string"]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/cssMode-CjiAH6dQ",["module","exports","./workers-DcJshg-q","./lspLanguageFeatures-kM9O9rjY","./editor.api-CalNCsUg"],(function(a,t,f,e,d){"use strict";class m{constructor(c){this._defaults=c,this._worker=null,this._client=null,this._idleCheckInterval=window.setInterval(()=>this._checkIfIdle(),30*1e3),this._lastUsedTime=0,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker())}_stopWorker(){this._worker&&(this._worker.dispose(),this._worker=null),this._client=null}dispose(){clearInterval(this._idleCheckInterval),this._configChangeListener.dispose(),this._stopWorker()}_checkIfIdle(){if(!this._worker)return;Date.now()-this._lastUsedTime>12e4&&this._stopWorker()}_getClient(){return this._lastUsedTime=Date.now(),this._client||(this._worker=f.createWebWorker({moduleId:"vs/language/css/cssWorker",createWorker:()=>new Worker(new URL(""+new URL(require.toUrl("./assets/css.worker-HnVq6Ewq.js"),document.baseURI).href,new URL(a.uri,document.baseURI).href),{type:"module"}),label:this._defaults.languageId,createData:{options:this._defaults.options,languageId:this._defaults.languageId}}),this._client=this._worker.getProxy()),this._client}getLanguageServiceWorker(...c){let i;return this._getClient().then(h=>{i=h}).then(h=>{if(this._worker)return this._worker.withSyncedResources(c)}).then(h=>i)}}function w(s){const c=[],i=[],h=new m(s);c.push(h);const o=(...n)=>h.getLanguageServiceWorker(...n);function A(){const{languageId:n,modeConfiguration:r}=s;g(i),r.completionItems&&i.push(d.languages.registerCompletionItemProvider(n,new e.CompletionAdapter(o,["/","-",":"]))),r.hovers&&i.push(d.languages.registerHoverProvider(n,new e.HoverAdapter(o))),r.documentHighlights&&i.push(d.languages.registerDocumentHighlightProvider(n,new e.DocumentHighlightAdapter(o))),r.definitions&&i.push(d.languages.registerDefinitionProvider(n,new e.DefinitionAdapter(o))),r.references&&i.push(d.languages.registerReferenceProvider(n,new e.ReferenceAdapter(o))),r.documentSymbols&&i.push(d.languages.registerDocumentSymbolProvider(n,new e.DocumentSymbolAdapter(o))),r.rename&&i.push(d.languages.registerRenameProvider(n,new e.RenameAdapter(o))),r.colors&&i.push(d.languages.registerColorProvider(n,new e.DocumentColorAdapter(o))),r.foldingRanges&&i.push(d.languages.registerFoldingRangeProvider(n,new e.FoldingRangeAdapter(o))),r.diagnostics&&i.push(new e.DiagnosticsAdapter(n,o,s.onDidChange)),r.selectionRanges&&i.push(d.languages.registerSelectionRangeProvider(n,new e.SelectionRangeAdapter(o))),r.documentFormattingEdits&&i.push(d.languages.registerDocumentFormattingEditProvider(n,new e.DocumentFormattingEditProvider(o))),r.documentRangeFormattingEdits&&i.push(d.languages.registerDocumentRangeFormattingEditProvider(n,new e.DocumentRangeFormattingEditProvider(o)))}return A(),c.push(l(i)),l(c)}function l(s){return{dispose:()=>g(s)}}function g(s){for(;s.length;)s.pop().dispose()}t.CompletionAdapter=e.CompletionAdapter,t.DefinitionAdapter=e.DefinitionAdapter,t.DiagnosticsAdapter=e.DiagnosticsAdapter,t.DocumentColorAdapter=e.DocumentColorAdapter,t.DocumentFormattingEditProvider=e.DocumentFormattingEditProvider,t.DocumentHighlightAdapter=e.DocumentHighlightAdapter,t.DocumentLinkAdapter=e.DocumentLinkAdapter,t.DocumentRangeFormattingEditProvider=e.DocumentRangeFormattingEditProvider,t.DocumentSymbolAdapter=e.DocumentSymbolAdapter,t.FoldingRangeAdapter=e.FoldingRangeAdapter,t.HoverAdapter=e.HoverAdapter,t.ReferenceAdapter=e.ReferenceAdapter,t.RenameAdapter=e.RenameAdapter,t.SelectionRangeAdapter=e.SelectionRangeAdapter,t.fromPosition=e.fromPosition,t.fromRange=e.fromRange,t.toRange=e.toRange,t.toTextEdit=e.toTextEdit,t.WorkerManager=m,t.setupMode=w,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/cypher-DVThT8BS",["exports"],(function(e){"use strict";const i={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}]},t={defaultToken:"",tokenPostfix:".cypher",ignoreCase:!0,brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"}],keywords:["ALL","AND","AS","ASC","ASCENDING","BY","CALL","CASE","CONTAINS","CREATE","DELETE","DESC","DESCENDING","DETACH","DISTINCT","ELSE","END","ENDS","EXISTS","IN","IS","LIMIT","MANDATORY","MATCH","MERGE","NOT","ON","ON","OPTIONAL","OR","ORDER","REMOVE","RETURN","SET","SKIP","STARTS","THEN","UNION","UNWIND","WHEN","WHERE","WITH","XOR","YIELD"],builtinLiterals:["true","TRUE","false","FALSE","null","NULL"],builtinFunctions:["abs","acos","asin","atan","atan2","avg","ceil","coalesce","collect","cos","cot","count","degrees","e","endNode","exists","exp","floor","head","id","keys","labels","last","left","length","log","log10","lTrim","max","min","nodes","percentileCont","percentileDisc","pi","properties","radians","rand","range","relationships","replace","reverse","right","round","rTrim","sign","sin","size","split","sqrt","startNode","stDev","stDevP","substring","sum","tail","tan","timestamp","toBoolean","toFloat","toInteger","toLower","toString","toUpper","trim","type"],operators:["+","-","*","/","%","^","=","<>","<",">","<=",">=","->","<-","-->","<--"],escapes:/\\(?:[tbnrf\\"'`]|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+/,octaldigits:/[0-7]+/,hexdigits:/[0-9a-fA-F]+/,tokenizer:{root:[[/[{}[\]()]/,"@brackets"],{include:"common"}],common:[{include:"@whitespace"},{include:"@numbers"},{include:"@strings"},[/:[a-zA-Z_][\w]*/,"type.identifier"],[/[a-zA-Z_][\w]*(?=\()/,{cases:{"@builtinFunctions":"predefined.function"}}],[/[a-zA-Z_$][\w$]*/,{cases:{"@keywords":"keyword","@builtinLiterals":"predefined.literal","@default":"identifier"}}],[/`/,"identifier.escape","@identifierBacktick"],[/[;,.:|]/,"delimiter"],[/[<>=%+\-*/^]+/,{cases:{"@operators":"delimiter","@default":""}}]],numbers:[[/-?(@digits)[eE](-?(@digits))?/,"number.float"],[/-?(@digits)?\.(@digits)([eE]-?(@digits))?/,"number.float"],[/-?0x(@hexdigits)/,"number.hex"],[/-?0(@octaldigits)/,"number.octal"],[/-?(@digits)/,"number"]],strings:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string","@stringDouble"],[/'/,"string","@stringSingle"]],whitespace:[[/[ \t\r\n]+/,"white"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/\/\/.*/,"comment"],[/[^/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[/*]/,"comment"]],stringDouble:[[/[^\\"]+/,"string"],[/@escapes/,"string"],[/\\./,"string.invalid"],[/"/,"string","@pop"]],stringSingle:[[/[^\\']+/,"string"],[/@escapes/,"string"],[/\\./,"string.invalid"],[/'/,"string","@pop"]],identifierBacktick:[[/[^\\`]+/,"identifier.escape"],[/@escapes/,"identifier.escape"],[/\\./,"identifier.escape.invalid"],[/`/,"identifier.escape","@pop"]]}};e.conf=i,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/dart-CmGfCvrO",["exports"],(function(e){"use strict";const n={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string"]},{open:"`",close:"`",notIn:["string","comment"]},{open:"/**",close:" */",notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:"(",close:")"},{open:'"',close:'"'},{open:"`",close:"`"}],folding:{markers:{start:/^\s*\s*#?region\b/,end:/^\s*\s*#?endregion\b/}}},t={defaultToken:"invalid",tokenPostfix:".dart",keywords:["abstract","dynamic","implements","show","as","else","import","static","assert","enum","in","super","async","export","interface","switch","await","extends","is","sync","break","external","library","this","case","factory","mixin","throw","catch","false","new","true","class","final","null","try","const","finally","on","typedef","continue","for","operator","var","covariant","Function","part","void","default","get","rethrow","while","deferred","hide","return","with","do","if","set","yield"],typeKeywords:["int","double","String","bool"],operators:["+","-","*","/","~/","%","++","--","==","!=",">","<",">=","<=","=","-=","/=","%=",">>=","^=","+=","*=","~/=","<<=","&=","!=","||","&&","&","|","^","~","<<",">>","!",">>>","??","?",":","|="],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,regexpctl:/[(){}\[\]\$\^|\-*+?\.]/,regexpesc:/\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/,tokenizer:{root:[[/[{}]/,"delimiter.bracket"],{include:"common"}],common:[[/[a-z_$][\w$]*/,{cases:{"@typeKeywords":"type.identifier","@keywords":"keyword","@default":"identifier"}}],[/[A-Z_$][\w\$]*/,"type.identifier"],{include:"@whitespace"},[/\/(?=([^\\\/]|\\.)+\/([gimsuy]*)(\s*)(\.|;|,|\)|\]|\}|$))/,{token:"regexp",bracket:"@open",next:"@regexp"}],[/@[a-zA-Z]+/,"annotation"],[/[()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/!(?=([^=]|$))/,"delimiter"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/(@digits)[eE]([\-+]?(@digits))?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?/,"number.float"],[/0[xX](@hexdigits)n?/,"number.hex"],[/0[oO]?(@octaldigits)n?/,"number.octal"],[/0[bB](@binarydigits)n?/,"number.binary"],[/(@digits)n?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string_double"],[/'/,"string","@string_single"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@jsdoc"],[/\/\*/,"comment","@comment"],[/\/\/\/.*$/,"comment.doc"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],jsdoc:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],regexp:[[/(\{)(\d+(?:,\d*)?)(\})/,["regexp.escape.control","regexp.escape.control","regexp.escape.control"]],[/(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/,["regexp.escape.control",{token:"regexp.escape.control",next:"@regexrange"}]],[/(\()(\?:|\?=|\?!)/,["regexp.escape.control","regexp.escape.control"]],[/[()]/,"regexp.escape.control"],[/@regexpctl/,"regexp.escape.control"],[/[^\\\/]/,"regexp"],[/@regexpesc/,"regexp.escape"],[/\\\./,"regexp.invalid"],[/(\/)([gimsuy]*)/,[{token:"regexp",bracket:"@close",next:"@pop"},"keyword.other"]]],regexrange:[[/-/,"regexp.escape.control"],[/\^/,"regexp.invalid"],[/@regexpesc/,"regexp.escape"],[/[^\]]/,"regexp"],[/\]/,{token:"regexp.escape.control",next:"@pop",bracket:"@close"}]],string_double:[[/[^\\"\$]+/,"string"],[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"],[/\$\w+/,"identifier"]],string_single:[[/[^\\'\$]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/'/,"string","@pop"],[/\$\w+/,"identifier"]]}};e.conf=n,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/dockerfile-CZqqYdch",["exports"],(function(e){"use strict";const o={brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},n={defaultToken:"",tokenPostfix:".dockerfile",variable:/\${?[\w]+}?/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/(ONBUILD)(\s+)/,["keyword",""]],[/(ENV)(\s+)([\w]+)/,["keyword","",{token:"variable",next:"@arguments"}]],[/(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|ARG|VOLUME|LABEL|USER|WORKDIR|COPY|CMD|STOPSIGNAL|SHELL|HEALTHCHECK|ENTRYPOINT)/,{token:"keyword",next:"@arguments"}]],arguments:[{include:"@whitespace"},{include:"@strings"},[/(@variable)/,{cases:{"@eos":{token:"variable",next:"@popall"},"@default":"variable"}}],[/\\/,{cases:{"@eos":"","@default":""}}],[/./,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],whitespace:[[/\s+/,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],comment:[[/(^#.*$)/,"comment","@popall"]],strings:[[/\\'$/,"","@popall"],[/\\'/,""],[/'$/,"string","@popall"],[/'/,"string","@stringBody"],[/"$/,"string","@popall"],[/"/,"string","@dblStringBody"]],stringBody:[[/[^\\\$']/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/'$/,"string","@popall"],[/'/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]],dblStringBody:[[/[^\\\$"]/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/"$/,"string","@popall"],[/"/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]]}};e.conf=o,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/flow9-DqtmStfK",["exports"],(function(e){"use strict";const o={comments:{blockComment:["/*","*/"],lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string"]},{open:"[",close:"]",notIn:["string"]},{open:"(",close:")",notIn:["string"]},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}]},n={defaultToken:"",tokenPostfix:".flow",keywords:["import","require","export","forbid","native","if","else","cast","unsafe","switch","default"],types:["io","mutable","bool","int","double","string","flow","void","ref","true","false","with"],operators:["=",">","<","<=",">=","==","!","!=",":=","::=","&&","||","+","-","*","/","@","&","%",":","->","\\","$","??","^"],symbols:/[@$=><!~?:&|+\-*\\\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":"keyword","@types":"type","@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"delimiter"],[/[<>](?!@symbols)/,"delimiter"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}};e.conf=o,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/fsharp-BOMdg4U1",["exports"],(function(e){"use strict";const n={comments:{lineComment:"//",blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*//\\s*#region\\b|^\\s*\\(\\*\\s*#region(.*)\\*\\)"),end:new RegExp("^\\s*//\\s*#endregion\\b|^\\s*\\(\\*\\s*#endregion\\s*\\*\\)")}}},t={defaultToken:"",tokenPostfix:".fs",keywords:["abstract","and","atomic","as","assert","asr","base","begin","break","checked","component","const","constraint","constructor","continue","class","default","delegate","do","done","downcast","downto","elif","else","end","exception","eager","event","external","extern","false","finally","for","fun","function","fixed","functor","global","if","in","include","inherit","inline","interface","internal","land","lor","lsl","lsr","lxor","lazy","let","match","member","mod","module","mutable","namespace","method","mixin","new","not","null","of","open","or","object","override","private","parallel","process","protected","pure","public","rec","return","static","sealed","struct","sig","then","to","true","tailcall","trait","try","type","upcast","use","val","void","virtual","volatile","when","while","with","yield"],symbols:/[=><!~?:&|+\-*\^%;\.,\/]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,integersuffix:/[uU]?[yslnLI]?/,floatsuffix:/[fFmM]?/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/\[<.*>\]/,"annotation"],[/^#(if|else|endif)/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0x[0-9a-fA-F]+LF/,"number.float"],[/0x[0-9a-fA-F]+(@integersuffix)/,"number.hex"],[/0b[0-1]+(@integersuffix)/,"number.bin"],[/\d+(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string",'@string."""'],[/"/,"string",'@string."'],[/\@"/,{token:"string.quote",next:"@litstring"}],[/'[^\\']'B?/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\(\*(?!\))/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^*(]+/,"comment"],[/\*\)/,"comment","@pop"],[/\*/,"comment"],[/\(\*\)/,"comment"],[/\(/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/("""|"B?)/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]]}};e.conf=n,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/go-D_hbi-Jt",["exports"],(function(e){"use strict";const n={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`",notIn:["string"]},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`"},{open:'"',close:'"'},{open:"'",close:"'"}]},o={defaultToken:"",tokenPostfix:".go",keywords:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var","bool","true","false","uint8","uint16","uint32","uint64","int8","int16","int32","int64","float32","float64","complex64","complex128","byte","rune","uint","int","uintptr","string","nil"],operators:["+","-","*","/","%","&","|","^","<<",">>","&^","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=","&^=","&&","||","<-","++","--","==","<",">","=","!","!=","<=",">=",":=","...","(",")","","]","{","}",",",";",".",":"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/\[\[.*\]\]/,"annotation"],[/^\s*#\w+/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex"],[/0[0-7']*[0-7]/,"number.octal"],[/0[bB][0-1']*[0-1]/,"number.binary"],[/\d[\d']*/,"number"],[/\d/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/`/,"string","@rawstring"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],rawstring:[[/[^\`]/,"string"],[/`/,"string","@pop"]]}};e.conf=n,e.language=o,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/graphql-CKUU4kLG",["exports"],(function(e){"use strict";const n={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"""',close:'"""',notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"""',close:'"""'},{open:'"',close:'"'}],folding:{offSide:!0}},t={defaultToken:"invalid",tokenPostfix:".gql",keywords:["null","true","false","query","mutation","subscription","extend","schema","directive","scalar","type","interface","union","enum","input","implements","fragment","on"],typeKeywords:["Int","Float","String","Boolean","ID"],directiveLocations:["SCHEMA","SCALAR","OBJECT","FIELD_DEFINITION","ARGUMENT_DEFINITION","INTERFACE","UNION","ENUM","ENUM_VALUE","INPUT_OBJECT","INPUT_FIELD_DEFINITION","QUERY","MUTATION","SUBSCRIPTION","FIELD","FRAGMENT_DEFINITION","FRAGMENT_SPREAD","INLINE_FRAGMENT","VARIABLE_DEFINITION"],operators:["=","!","?",":","&","|"],symbols:/[=!?:&|]+/,escapes:/\\(?:["\\\/bfnrt]|u[0-9A-Fa-f]{4})/,tokenizer:{root:[[/[a-z_][\w$]*/,{cases:{"@keywords":"keyword","@default":"key.identifier"}}],[/[$][\w$]*/,{cases:{"@keywords":"keyword","@default":"argument.identifier"}}],[/[A-Z][\w\$]*/,{cases:{"@typeKeywords":"keyword","@default":"type.identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"operator","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,{token:"annotation",log:"annotation token: $0"}],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"],[/"""/,{token:"string",next:"@mlstring",nextEmbedded:"markdown"}],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,{token:"string.quote",bracket:"@open",next:"@string"}]],mlstring:[[/[^"]+/,"string"],['"""',{token:"string",next:"@pop",nextEmbedded:"@pop"}]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",bracket:"@close",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/#.*$/,"comment"]]}};e.conf=n,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/hcl-DTaboeZW",["exports"],(function(e){"use strict";const t={comments:{lineComment:"#",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"',notIn:["string"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}]},s={defaultToken:"",tokenPostfix:".hcl",keywords:["var","local","path","for_each","any","string","number","bool","true","false","null","if ","else ","endif ","for ","in","endfor"],operators:["=",">=","<=","==","!=","+","-","*","/","%","&&","||","!","<",">","?","...",":"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,terraformFunctions:/(abs|ceil|floor|log|max|min|pow|signum|chomp|format|formatlist|indent|join|lower|regex|regexall|replace|split|strrev|substr|title|trimspace|upper|chunklist|coalesce|coalescelist|compact|concat|contains|distinct|element|flatten|index|keys|length|list|lookup|map|matchkeys|merge|range|reverse|setintersection|setproduct|setunion|slice|sort|transpose|values|zipmap|base64decode|base64encode|base64gzip|csvdecode|jsondecode|jsonencode|urlencode|yamldecode|yamlencode|abspath|dirname|pathexpand|basename|file|fileexists|fileset|filebase64|templatefile|formatdate|timeadd|timestamp|base64sha256|base64sha512|bcrypt|filebase64sha256|filebase64sha512|filemd5|filemd1|filesha256|filesha512|md5|rsadecrypt|sha1|sha256|sha512|uuid|uuidv5|cidrhost|cidrnetmask|cidrsubnet|tobool|tolist|tomap|tonumber|toset|tostring)/,terraformMainBlocks:/(module|data|terraform|resource|provider|variable|output|locals)/,tokenizer:{root:[[/^@terraformMainBlocks([ \t]*)([\w-]+|"[\w-]+"|)([ \t]*)([\w-]+|"[\w-]+"|)([ \t]*)(\{)/,["type","","string","","string","","@brackets"]],[/(\w+[ \t]+)([ \t]*)([\w-]+|"[\w-]+"|)([ \t]*)([\w-]+|"[\w-]+"|)([ \t]*)(\{)/,["identifier","","string","","string","","@brackets"]],[/(\w+[ \t]+)([ \t]*)([\w-]+|"[\w-]+"|)([ \t]*)([\w-]+|"[\w-]+"|)(=)(\{)/,["identifier","","string","","operator","","@brackets"]],{include:"@terraform"}],terraform:[[/@terraformFunctions(\()/,["type","@brackets"]],[/[a-zA-Z_]\w*-*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"variable"}}],{include:"@whitespace"},{include:"@heredoc"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"operator","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/\d[\d']*/,"number"],[/\d/,"number"],[/[;,.]/,"delimiter"],[/"/,"string","@string"],[/'/,"invalid"]],heredoc:[[/<<[-]*\s*["]?([\w\-]+)["]?/,{token:"string.heredoc.delimiter",next:"@heredocBody.$1"}]],heredocBody:[[/([\w\-]+)$/,{cases:{"$1==$S2":[{token:"string.heredoc.delimiter",next:"@popall"}],"@default":"string.heredoc"}}],[/./,"string.heredoc"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"],[/#.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],string:[[/\$\{/,{token:"delimiter",next:"@stringExpression"}],[/[^\\"\$]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@popall"]],stringInsideExpression:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],stringExpression:[[/\}/,{token:"delimiter",next:"@pop"}],[/"/,"string","@stringInsideExpression"],{include:"@terraform"}]}};e.conf=t,e.language=s,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/html-Pa1xEWsY",["exports","./editor.api-CalNCsUg"],(function(e,t){"use strict";const n=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"],i={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,comments:{blockComment:["<!--","-->"]},brackets:[["<!--","-->"],["<",">"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:'"',close:'"'},{open:"'",close:"'"},{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"}],onEnterRules:[{beforeText:new RegExp(`<(?!(?:${n.join("|")}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`,"i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:t.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp(`<(?!(?:${n.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,"i"),action:{indentAction:t.languages.IndentAction.Indent}}],folding:{markers:{start:new RegExp("^\\s*<!--\\s*#region\\b.*-->"),end:new RegExp("^\\s*<!--\\s*#endregion\\b.*-->")}}},r={defaultToken:"",tokenPostfix:".html",ignoreCase:!0,tokenizer:{root:[[/<!DOCTYPE/,"metatag","@doctype"],[/<!--/,"comment","@comment"],[/(<)((?:[\w\-]+:)?[\w\-]+)(\s*)(\/>)/,["delimiter","tag","","delimiter"]],[/(<)(script)/,["delimiter",{token:"tag",next:"@script"}]],[/(<)(style)/,["delimiter",{token:"tag",next:"@style"}]],[/(<)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/(<\/)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/</,"delimiter"],[/[^<]+/]],doctype:[[/[^>]+/,"metatag.content"],[/>/,"metatag","@pop"]],comment:[[/-->/,"comment","@pop"],[/[^-]+/,"comment.content"],[/./,"comment.content"]],otherTag:[[/\/?>/,"delimiter","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],scriptAfterType:[[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/"module"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.text/javascript"}],[/'module'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.text/javascript"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/>/,{token:"delimiter",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]],style:[[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],styleAfterType:[[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/>/,{token:"delimiter",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]]}};e.conf=i,e.language=r,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/htmlMode-Bz67EXwp",["module","exports","./workers-DcJshg-q","./lspLanguageFeatures-kM9O9rjY","./editor.api-CalNCsUg"],(function(u,n,v,e,i){"use strict";class l{constructor(d){this._defaults=d,this._worker=null,this._client=null,this._idleCheckInterval=window.setInterval(()=>this._checkIfIdle(),30*1e3),this._lastUsedTime=0,this._configChangeListener=this._defaults.onDidChange(()=>this._stopWorker())}_stopWorker(){this._worker&&(this._worker.dispose(),this._worker=null),this._client=null}dispose(){clearInterval(this._idleCheckInterval),this._configChangeListener.dispose(),this._stopWorker()}_checkIfIdle(){if(!this._worker)return;Date.now()-this._lastUsedTime>12e4&&this._stopWorker()}_getClient(){return this._lastUsedTime=Date.now(),this._client||(this._worker=v.createWebWorker({moduleId:"vs/language/html/htmlWorker",createWorker:()=>new Worker(new URL(""+new URL(require.toUrl("./assets/html.worker-B51mlPHg.js"),document.baseURI).href,new URL(u.uri,document.baseURI).href),{type:"module"}),createData:{languageSettings:this._defaults.options,languageId:this._defaults.languageId},label:this._defaults.languageId}),this._client=this._worker.getProxy()),this._client}getLanguageServiceWorker(...d){let t;return this._getClient().then(r=>{t=r}).then(r=>{if(this._worker)return this._worker.withSyncedResources(d)}).then(r=>t)}}class s extends e.CompletionAdapter{constructor(d){super(d,[".",":","<",'"',"=","/"])}}function w(o){const d=new l(o),t=(...c)=>d.getLanguageServiceWorker(...c);let r=o.languageId;i.languages.registerCompletionItemProvider(r,new s(t)),i.languages.registerHoverProvider(r,new e.HoverAdapter(t)),i.languages.registerDocumentHighlightProvider(r,new e.DocumentHighlightAdapter(t)),i.languages.registerLinkProvider(r,new e.DocumentLinkAdapter(t)),i.languages.registerFoldingRangeProvider(r,new e.FoldingRangeAdapter(t)),i.languages.registerDocumentSymbolProvider(r,new e.DocumentSymbolAdapter(t)),i.languages.registerSelectionRangeProvider(r,new e.SelectionRangeAdapter(t)),i.languages.registerRenameProvider(r,new e.RenameAdapter(t)),r==="html"&&(i.languages.registerDocumentFormattingEditProvider(r,new e.DocumentFormattingEditProvider(t)),i.languages.registerDocumentRangeFormattingEditProvider(r,new e.DocumentRangeFormattingEditProvider(t)))}function f(o){const d=[],t=[],r=new l(o);d.push(r);const c=(...g)=>r.getLanguageServiceWorker(...g);function A(){const{languageId:g,modeConfiguration:m}=o;a(t),m.completionItems&&t.push(i.languages.registerCompletionItemProvider(g,new s(c))),m.hovers&&t.push(i.languages.registerHoverProvider(g,new e.HoverAdapter(c))),m.documentHighlights&&t.push(i.languages.registerDocumentHighlightProvider(g,new e.DocumentHighlightAdapter(c))),m.links&&t.push(i.languages.registerLinkProvider(g,new e.DocumentLinkAdapter(c))),m.documentSymbols&&t.push(i.languages.registerDocumentSymbolProvider(g,new e.DocumentSymbolAdapter(c))),m.rename&&t.push(i.languages.registerRenameProvider(g,new e.RenameAdapter(c))),m.foldingRanges&&t.push(i.languages.registerFoldingRangeProvider(g,new e.FoldingRangeAdapter(c))),m.selectionRanges&&t.push(i.languages.registerSelectionRangeProvider(g,new e.SelectionRangeAdapter(c))),m.documentFormattingEdits&&t.push(i.languages.registerDocumentFormattingEditProvider(g,new e.DocumentFormattingEditProvider(c))),m.documentRangeFormattingEdits&&t.push(i.languages.registerDocumentRangeFormattingEditProvider(g,new e.DocumentRangeFormattingEditProvider(c)))}return A(),d.push(h(t)),h(d)}function h(o){return{dispose:()=>a(o)}}function a(o){for(;o.length;)o.pop().dispose()}n.CompletionAdapter=e.CompletionAdapter,n.DefinitionAdapter=e.DefinitionAdapter,n.DiagnosticsAdapter=e.DiagnosticsAdapter,n.DocumentColorAdapter=e.DocumentColorAdapter,n.DocumentFormattingEditProvider=e.DocumentFormattingEditProvider,n.DocumentHighlightAdapter=e.DocumentHighlightAdapter,n.DocumentLinkAdapter=e.DocumentLinkAdapter,n.DocumentRangeFormattingEditProvider=e.DocumentRangeFormattingEditProvider,n.DocumentSymbolAdapter=e.DocumentSymbolAdapter,n.FoldingRangeAdapter=e.FoldingRangeAdapter,n.HoverAdapter=e.HoverAdapter,n.ReferenceAdapter=e.ReferenceAdapter,n.RenameAdapter=e.RenameAdapter,n.SelectionRangeAdapter=e.SelectionRangeAdapter,n.fromPosition=e.fromPosition,n.fromRange=e.fromRange,n.toRange=e.toRange,n.toTextEdit=e.toTextEdit,n.WorkerManager=l,n.setupMode=f,n.setupMode1=w,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/ini-CsNwO04R",["exports"],(function(e){"use strict";const n={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},s={defaultToken:"",tokenPostfix:".ini",escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^\[[^\]]*\]/,"metatag"],[/(^\w+)(\s*)(\=)/,["key","","delimiter"]],{include:"@whitespace"},[/\d+/,"number"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],whitespace:[[/[ \t\r\n]+/,""],[/^\s*[#;].*$/,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}};e.conf=n,e.language=s,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/java-CI4ZMsH9",["exports"],(function(e){"use strict";const t={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:<editor-fold\\b))"),end:new RegExp("^\\s*//\\s*(?:(?:#?endregion\\b)|(?:</editor-fold>))")}}},n={defaultToken:"",tokenPostfix:".java",keywords:["abstract","continue","for","new","switch","assert","default","goto","package","synchronized","boolean","do","if","private","this","break","double","implements","protected","throw","byte","else","import","public","throws","case","enum","instanceof","return","transient","catch","extends","int","short","try","char","final","interface","static","void","class","finally","long","strictfp","volatile","const","float","native","super","while","true","false","yield","record","sealed","non-sealed","permits"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,tokenizer:{root:[["non-sealed","keyword.non-sealed"],[/[a-zA-Z_$][\w$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/0[xX](@hexdigits)[Ll]?/,"number.hex"],[/0(@octaldigits)[Ll]?/,"number.octal"],[/0[bB](@binarydigits)[Ll]?/,"number.binary"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string","@multistring"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],multistring:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"""/,"string","@pop"],[/./,"string"]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/javascript-PczUCGdz",["exports","./typescript-DfOrAzoV"],(function(a,e){"use strict";const n=e.conf,g={defaultToken:"invalid",tokenPostfix:".js",keywords:["break","case","catch","class","continue","const","constructor","debugger","default","delete","do","else","export","extends","false","finally","for","from","function","get","if","import","in","instanceof","let","new","null","return","set","static","super","switch","symbol","this","throw","true","try","typeof","undefined","var","void","while","with","yield","async","await","of"],typeKeywords:[],operators:e.language.operators,symbols:e.language.symbols,escapes:e.language.escapes,digits:e.language.digits,octaldigits:e.language.octaldigits,binarydigits:e.language.binarydigits,hexdigits:e.language.hexdigits,regexpctl:e.language.regexpctl,regexpesc:e.language.regexpesc,tokenizer:e.language.tokenizer};a.conf=n,a.language=g,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/kotlin-IUYPiTV8",["exports"],(function(e){"use strict";const n={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}],folding:{markers:{start:new RegExp("^\\s*//\\s*(?:(?:#?region\\b)|(?:<editor-fold\\b))"),end:new RegExp("^\\s*//\\s*(?:(?:#?endregion\\b)|(?:</editor-fold>))")}}},t={defaultToken:"",tokenPostfix:".kt",keywords:["as","as?","break","class","continue","do","else","false","for","fun","if","in","!in","interface","is","!is","null","object","package","return","super","this","throw","true","try","typealias","val","var","when","while","by","catch","constructor","delegate","dynamic","field","file","finally","get","import","init","param","property","receiver","set","setparam","where","actual","abstract","annotation","companion","const","crossinline","data","enum","expect","external","final","infix","inline","inner","internal","lateinit","noinline","open","operator","out","override","private","protected","public","reified","sealed","suspend","tailrec","vararg","field","it"],operators:["+","-","*","/","%","=","+=","-=","*=","/=","%=","++","--","&&","||","!","==","!=","===","!==",">","<","<=",">=","[","]","!!","?.","?:","::","..",":","?","->","@",";","$","_"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,tokenizer:{root:[[/[A-Z][\w\$]*/,"type.identifier"],[/[a-zA-Z_$][\w$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fF]?/,"number.float"],[/(@digits)?\.(@digits)([eE][\-+]?(@digits))?[fF]?/,"number.float"],[/0[xX](@hexdigits)[uU]?L?/,"number.hex"],[/0[bB](@binarydigits)[uU]?L?/,"number.binary"],[/(@digits)[fF]/,"number.float"],[/(@digits)[uU]?L?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string","@multistring"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\/\*/,"comment","@comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc","@push"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],multistring:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"""/,"string","@pop"],[/./,"string"]]}};e.conf=n,e.language=t,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/language/css/monaco.contribution",["exports","../../editor.api-CalNCsUg","../../monaco.contribution-DO3azKX8"],(function(s,t,e){"use strict";s.cssDefaults=e.cssDefaults,s.lessDefaults=e.lessDefaults,s.scssDefaults=e.scssDefaults,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/language/html/monaco.contribution",["exports","../../editor.api-CalNCsUg","../../monaco.contribution-qLAYrEOP"],(function(e,r,a){"use strict";e.handlebarDefaults=a.handlebarDefaults,e.handlebarLanguageService=a.handlebarLanguageService,e.htmlDefaults=a.htmlDefaults,e.htmlLanguageService=a.htmlLanguageService,e.razorDefaults=a.razorDefaults,e.razorLanguageService=a.razorLanguageService,e.registerHTMLLanguageService=a.registerHTMLLanguageService,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/language/json/monaco.contribution",["exports","../../editor.api-CalNCsUg","../../monaco.contribution-EcChJV6a"],(function(e,t,o){"use strict";e.getWorker=o.getWorker,e.jsonDefaults=o.jsonDefaults,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/language/typescript/monaco.contribution",["exports","../../monaco.contribution-D2OdxNBt","../../editor.api-CalNCsUg"],(function(e,d,i){"use strict";e.JsxEmit=d.JsxEmit,e.ModuleKind=d.ModuleKind,e.ModuleResolutionKind=d.ModuleResolutionKind,e.NewLineKind=d.NewLineKind,e.ScriptTarget=d.ScriptTarget,e.getJavaScriptWorker=d.getJavaScriptWorker,e.getTypeScriptWorker=d.getTypeScriptWorker,e.javascriptDefaults=d.javascriptDefaults,e.typescriptDefaults=d.typescriptDefaults,e.typescriptVersion=d.typescriptVersion,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1,2 @@
|
||||
define("vs/less-C0eDYdqa",["exports"],(function(e){"use strict";const t={wordPattern:/(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,comments:{blockComment:["/*","*/"],lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*\\/\\*\\s*#region\\b\\s*(.*?)\\s*\\*\\/"),end:new RegExp("^\\s*\\/\\*\\s*#endregion\\b.*\\*\\/")}}},n={defaultToken:"",tokenPostfix:".less",identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",identifierPlus:"-?-?([a-zA-Z:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@nestedJSBegin"},["[ \\t\\r\\n]+",""],{include:"@comments"},{include:"@keyword"},{include:"@strings"},{include:"@numbers"},["[*_]?[a-zA-Z\\-\\s]+(?=:.*(;|(\\\\$)))","attribute.name","@attribute"],["url(\\-prefix)?\\(",{token:"tag",next:"@urldeclaration"}],["[{}()\\[\\]]","@brackets"],["[,:;]","delimiter"],["#@identifierPlus","tag.id"],["&","tag"],["\\.@identifierPlus(?=\\()","tag.class","@attribute"],["\\.@identifierPlus","tag.class"],["@identifierPlus","tag"],{include:"@operators"},["@(@identifier(?=[:,\\)]))","variable","@attribute"],["@(@identifier)","variable"],["@","key","@atRules"]],nestedJSBegin:[["``","delimiter.backtick"],["`",{token:"delimiter.backtick",next:"@nestedJSEnd",nextEmbedded:"text/javascript"}]],nestedJSEnd:[["`",{token:"delimiter.backtick",next:"@pop",nextEmbedded:"@pop"}]],operators:[["[<>=\\+\\-\\*\\/\\^\\|\\~]","operator"]],keyword:[["(@[\\s]*import|![\\s]*important|true|false|when|iscolor|isnumber|isstring|iskeyword|isurl|ispixel|ispercentage|isem|hue|saturation|lightness|alpha|lighten|darken|saturate|desaturate|fadein|fadeout|fade|spin|mix|round|ceil|floor|percentage)\\b","keyword"]],urldeclaration:[{include:"@strings"},[`[^)\r
|
||||
]+`,"string"],["\\)",{token:"tag",next:"@pop"}]],attribute:[{include:"@nestedJSBegin"},{include:"@comments"},{include:"@strings"},{include:"@numbers"},{include:"@keyword"},["[a-zA-Z\\-]+(?=\\()","attribute.value","@attribute"],[">","operator","@pop"],["@identifier","attribute.value"],{include:"@operators"},["@(@identifier)","variable"],["[)\\}]","@brackets","@pop"],["[{}()\\[\\]>]","@brackets"],["[;]","delimiter","@pop"],["[,=:]","delimiter"],["\\s",""],[".","attribute.value"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[["(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|fr|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],strings:[['~?"',{token:"string.delimiter",next:"@stringsEndDoubleQuote"}],["~?'",{token:"string.delimiter",next:"@stringsEndQuote"}]],stringsEndDoubleQuote:[['\\\\"',"string"],['"',{token:"string.delimiter",next:"@popall"}],[".","string"]],stringsEndQuote:[["\\\\'","string"],["'",{token:"string.delimiter",next:"@popall"}],[".","string"]],atRules:[{include:"@comments"},{include:"@strings"},["[()]","delimiter"],["[\\{;]","delimiter","@pop"],[".","key"]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/lexon-iON-Kj97",["exports"],(function(e){"use strict";const t={comments:{lineComment:"COMMENT"},brackets:[["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:":",close:"."}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`"},{open:'"',close:'"'},{open:"'",close:"'"},{open:":",close:"."}],folding:{markers:{start:new RegExp("^\\s*(::\\s*|COMMENT\\s+)#region"),end:new RegExp("^\\s*(::\\s*|COMMENT\\s+)#endregion")}}},i={tokenPostfix:".lexon",ignoreCase:!0,keywords:["lexon","lex","clause","terms","contracts","may","pay","pays","appoints","into","to"],typeKeywords:["amount","person","key","time","date","asset","text"],operators:["less","greater","equal","le","gt","or","and","add","added","subtract","subtracted","multiply","multiplied","times","divide","divided","is","be","certified"],symbols:/[=><!~?:&|+\-*\/\^%]+/,tokenizer:{root:[[/^(\s*)(comment:?(?:\s.*|))$/,["","comment"]],[/"/,{token:"identifier.quote",bracket:"@open",next:"@quoted_identifier"}],["LEX$",{token:"keyword",bracket:"@open",next:"@identifier_until_period"}],["LEXON",{token:"keyword",bracket:"@open",next:"@semver"}],[":",{token:"delimiter",bracket:"@open",next:"@identifier_until_period"}],[/[a-z_$][\w$]*/,{cases:{"@operators":"operator","@typeKeywords":"keyword.type","@keywords":"keyword","@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\.\d*\.\d*/,"number.semver"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/\d+/,"number"],[/[;,.]/,"delimiter"]],quoted_identifier:[[/[^\\"]+/,"identifier"],[/"/,{token:"identifier.quote",bracket:"@close",next:"@pop"}]],space_identifier_until_period:[[":","delimiter"],[" ",{token:"white",next:"@identifier_rest"}]],identifier_until_period:[{include:"@whitespace"},[":",{token:"delimiter",next:"@identifier_rest"}],[/[^\\.]+/,"identifier"],[/\./,{token:"delimiter",bracket:"@close",next:"@pop"}]],identifier_rest:[[/[^\\.]+/,"identifier"],[/\./,{token:"delimiter",bracket:"@close",next:"@pop"}]],semver:[{include:"@whitespace"},[":","delimiter"],[/\d*\.\d*\.\d*/,{token:"number.semver",bracket:"@close",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,"white"]]}};e.conf=t,e.language=i,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/liquid-DqKjdPGy",["exports","./editor.api-CalNCsUg"],(function(e,t){"use strict";const i=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"],n={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,brackets:[["<!--","-->"],["<",">"],["{{","}}"],["{%","%}"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"%",close:"%"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"<",close:">"},{open:'"',close:'"'},{open:"'",close:"'"}],onEnterRules:[{beforeText:new RegExp(`<(?!(?:${i.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,"i"),afterText:/^<\/(\w[\w\d]*)\s*>$/i,action:{indentAction:t.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp(`<(?!(?:${i.join("|")}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,"i"),action:{indentAction:t.languages.IndentAction.Indent}}]},o={defaultToken:"",tokenPostfix:"",builtinTags:["if","else","elseif","endif","render","assign","capture","endcapture","case","endcase","comment","endcomment","cycle","decrement","for","endfor","include","increment","layout","raw","endraw","render","tablerow","endtablerow","unless","endunless"],builtinFilters:["abs","append","at_least","at_most","capitalize","ceil","compact","date","default","divided_by","downcase","escape","escape_once","first","floor","join","json","last","lstrip","map","minus","modulo","newline_to_br","plus","prepend","remove","remove_first","replace","replace_first","reverse","round","rstrip","size","slice","sort","sort_natural","split","strip","strip_html","strip_newlines","times","truncate","truncatewords","uniq","upcase","url_decode","url_encode","where"],constants:["true","false"],operators:["==","!=",">","<",">=","<="],symbol:/[=><!]+/,identifier:/[a-zA-Z_][\w]*/,tokenizer:{root:[[/\{\%\s*comment\s*\%\}/,"comment.start.liquid","@comment"],[/\{\{/,{token:"@rematch",switchTo:"@liquidState.root"}],[/\{\%/,{token:"@rematch",switchTo:"@liquidState.root"}],[/(<)([\w\-]+)(\/>)/,["delimiter.html","tag.html","delimiter.html"]],[/(<)([:\w]+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/(<\/)([\w\-]+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/</,"delimiter.html"],[/\{/,"delimiter.html"],[/[^<{]+/]],comment:[[/\{\%\s*endcomment\s*\%\}/,"comment.end.liquid","@pop"],[/./,"comment.content.liquid"]],otherTag:[[/\{\{/,{token:"@rematch",switchTo:"@liquidState.otherTag"}],[/\{\%/,{token:"@rematch",switchTo:"@liquidState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],liquidState:[[/\{\{/,"delimiter.output.liquid"],[/\}\}/,{token:"delimiter.output.liquid",switchTo:"@$S2.$S3"}],[/\{\%/,"delimiter.tag.liquid"],[/raw\s*\%\}/,"delimiter.tag.liquid","@liquidRaw"],[/\%\}/,{token:"delimiter.tag.liquid",switchTo:"@$S2.$S3"}],{include:"liquidRoot"}],liquidRaw:[[/^(?!\{\%\s*endraw\s*\%\}).+/],[/\{\%/,"delimiter.tag.liquid"],[/@identifier/],[/\%\}/,{token:"delimiter.tag.liquid",next:"@root"}]],liquidRoot:[[/\d+(\.\d+)?/,"number.liquid"],[/"[^"]*"/,"string.liquid"],[/'[^']*'/,"string.liquid"],[/\s+/],[/@symbol/,{cases:{"@operators":"operator.liquid","@default":""}}],[/\./],[/@identifier/,{cases:{"@constants":"keyword.liquid","@builtinFilters":"predefined.liquid","@builtinTags":"predefined.liquid","@default":"variable.liquid"}}],[/[^}|%]/,"variable.liquid"]]}};e.conf=n,e.language=o,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because it is too large
Load Diff
+4
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/lua-DtygF91M",["exports"],(function(e){"use strict";const n={comments:{lineComment:"--",blockComment:["--[[","]]"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},o={defaultToken:"",tokenPostfix:".lua",keywords:["and","break","do","else","elseif","end","false","for","function","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"],brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],operators:["+","-","*","/","%","^","#","==","~=","<=",">=","<",">","=",";",":",",",".","..","..."],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/[a-zA-Z_]\w*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/(,)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["delimiter","","key","","delimiter"]],[/({)(\s*)([a-zA-Z_]\w*)(\s*)(:)(?!:)/,["@brackets","","key","","delimiter"]],[/[{}()\[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F_]*[0-9a-fA-F]/,"number.hex"],[/\d+?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],whitespace:[[/[ \t\r\n]+/,""],[/--\[([=]*)\[/,"comment","@comment.$1"],[/--.*$/,"comment"]],comment:[[/[^\]]+/,"comment"],[/\]([=]*)\]/,{cases:{"$1==$S2":{token:"comment",next:"@pop"},"@default":"comment"}}],[/./,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}};e.conf=n,e.language=o,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/m3-CsR4AuFi",["exports"],(function(e){"use strict";const t={comments:{blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"[",close:"]"},{open:"{",close:"}"},{open:"(",close:")"},{open:"(*",close:"*)"},{open:"<*",close:"*>"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]}]},o={defaultToken:"",tokenPostfix:".m3",brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["AND","ANY","ARRAY","AS","BEGIN","BITS","BRANDED","BY","CASE","CONST","DIV","DO","ELSE","ELSIF","END","EVAL","EXCEPT","EXCEPTION","EXIT","EXPORTS","FINALLY","FOR","FROM","GENERIC","IF","IMPORT","IN","INTERFACE","LOCK","LOOP","METHODS","MOD","MODULE","NOT","OBJECT","OF","OR","OVERRIDES","PROCEDURE","RAISE","RAISES","READONLY","RECORD","REF","REPEAT","RETURN","REVEAL","SET","THEN","TO","TRY","TYPE","TYPECASE","UNSAFE","UNTIL","UNTRACED","VALUE","VAR","WHILE","WITH"],reservedConstNames:["ABS","ADR","ADRSIZE","BITSIZE","BYTESIZE","CEILING","DEC","DISPOSE","FALSE","FIRST","FLOAT","FLOOR","INC","ISTYPE","LAST","LOOPHOLE","MAX","MIN","NARROW","NEW","NIL","NUMBER","ORD","ROUND","SUBARRAY","TRUE","TRUNC","TYPECODE","VAL"],reservedTypeNames:["ADDRESS","ANY","BOOLEAN","CARDINAL","CHAR","EXTENDED","INTEGER","LONGCARD","LONGINT","LONGREAL","MUTEX","NULL","REAL","REFANY","ROOT","TEXT"],operators:["+","-","*","/","&","^","."],relations:["=","#","<","<=",">",">=","<:",":"],delimiters:["|","..","=>",",",";",":="],symbols:/[>=<#.,:;+\-*/&^]+/,escapes:/\\(?:[\\fnrt"']|[0-7]{3})/,tokenizer:{root:[[/_\w*/,"invalid"],[/[a-zA-Z][a-zA-Z0-9_]*/,{cases:{"@keywords":{token:"keyword.$0"},"@reservedConstNames":{token:"constant.reserved.$0"},"@reservedTypeNames":{token:"type.reserved.$0"},"@default":"identifier"}}],{include:"@whitespace"},[/[{}()\[\]]/,"@brackets"],[/[0-9]+\.[0-9]+(?:[DdEeXx][\+\-]?[0-9]+)?/,"number.float"],[/[0-9]+(?:\_[0-9a-fA-F]+)?L?/,"number"],[/@symbols/,{cases:{"@operators":"operators","@relations":"operators","@delimiters":"delimiter","@default":"invalid"}}],[/'[^\\']'/,"string.char"],[/(')(@escapes)(')/,["string.char","string.escape","string.char"]],[/'/,"invalid"],[/"([^"\\]|\\.)*$/,"invalid"],[/"/,"string.text","@text"]],text:[[/[^\\"]+/,"string.text"],[/@escapes/,"string.escape"],[/\\./,"invalid"],[/"/,"string.text","@pop"]],comment:[[/\(\*/,"comment","@push"],[/\*\)/,"comment","@pop"],[/./,"comment"]],pragma:[[/<\*/,"keyword.pragma","@push"],[/\*>/,"keyword.pragma","@pop"],[/./,"keyword.pragma"]],whitespace:[[/[ \t\r\n]+/,"white"],[/\(\*/,"comment","@comment"],[/<\*/,"keyword.pragma","@pragma"]]}};e.conf=t,e.language=o,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/markdown-C_rD0bIw",["exports"],(function(e){"use strict";const t={comments:{blockComment:["<!--","-->"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">",notIn:["string"]}],surroundingPairs:[{open:"(",close:")"},{open:"[",close:"]"},{open:"`",close:"`"}],folding:{markers:{start:new RegExp("^\\s*<!--\\s*#?region\\b.*-->"),end:new RegExp("^\\s*<!--\\s*#?endregion\\b.*-->")}}},n={defaultToken:"",tokenPostfix:".md",control:/[\\`*_\[\]{}()#+\-\.!]/,noncontrol:/[^\\`*_\[\]{}()#+\-\.!]/,escapes:/\\(?:@control)/,jsescapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,empty:["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param"],tokenizer:{root:[[/^\s*\|/,"@rematch","@table_header"],[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/,["white","keyword","keyword","keyword"]],[/^\s*(=+|\-+)\s*$/,"keyword"],[/^\s*((\*[ ]?)+)\s*$/,"meta.separator"],[/^\s*>+/,"comment"],[/^\s*([\*\-+:]|\d+\.)\s/,"keyword"],[/^(\t|[ ]{4})[^ ].*$/,"string"],[/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/,{token:"string",next:"@codeblock"}],[/^\s*```\s*((?:\w|[\/\-#])+).*$/,{token:"string",next:"@codeblockgh",nextEmbedded:"$1"}],[/^\s*```\s*$/,{token:"string",next:"@codeblock"}],{include:"@linecontent"}],table_header:[{include:"@table_common"},[/[^\|]+/,"keyword.table.header"]],table_body:[{include:"@table_common"},{include:"@linecontent"}],table_common:[[/\s*[\-:]+\s*/,{token:"keyword",switchTo:"table_body"}],[/^\s*\|/,"keyword.table.left"],[/^\s*[^\|]/,"@rematch","@pop"],[/^\s*$/,"@rematch","@pop"],[/\|/,{cases:{"@eos":"keyword.table.right","@default":"keyword.table.middle"}}]],codeblock:[[/^\s*~~~\s*$/,{token:"string",next:"@pop"}],[/^\s*```\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]],codeblockgh:[[/```\s*$/,{token:"string",next:"@pop",nextEmbedded:"@pop"}],[/[^`]+/,"variable.source"]],linecontent:[[/&\w+;/,"string.escape"],[/@escapes/,"escape"],[/\b__([^\\_]|@escapes|_(?!_))+__\b/,"strong"],[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/,"strong"],[/\b_[^_]+_\b/,"emphasis"],[/\*([^\\*]|@escapes)+\*/,"emphasis"],[/`([^\\`]|@escapes)+`/,"variable"],[/\{+[^}]+\}+/,"string.target"],[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/,["string.link","","string.link"]],[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/,"string.link"],{include:"html"}],html:[[/<(\w+)\/>/,"tag"],[/<(\w+)(\-|\w)*/,{cases:{"@empty":{token:"tag",next:"@tag.$1"},"@default":{token:"tag",next:"@tag.$1"}}}],[/<\/(\w+)(\-|\w)*\s*>/,{token:"tag"}],[/<!--/,"comment","@comment"]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,"comment","@pop"],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]],tag:[[/[ \t\r\n]+/,"white"],[/(type)(\s*=\s*)(")([^"]+)(")/,["attribute.name.html","delimiter.html","string.html",{token:"string.html",switchTo:"@tag.$S2.$4"},"string.html"]],[/(type)(\s*=\s*)(')([^']+)(')/,["attribute.name.html","delimiter.html","string.html",{token:"string.html",switchTo:"@tag.$S2.$4"},"string.html"]],[/(\w+)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name.html","delimiter.html","string.html"]],[/\w+/,"attribute.name.html"],[/\/>/,"tag","@pop"],[/>/,{cases:{"$S2==style":{token:"tag",switchTo:"embeddedStyle",nextEmbedded:"text/css"},"$S2==script":{cases:{$S3:{token:"tag",switchTo:"embeddedScript",nextEmbedded:"$S3"},"@default":{token:"tag",switchTo:"embeddedScript",nextEmbedded:"text/javascript"}}},"@default":{token:"tag",next:"@pop"}}}]],embeddedStyle:[[/[^<]+/,""],[/<\/style\s*>/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/</,""]],embeddedScript:[[/[^<]+/,""],[/<\/script\s*>/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/</,""]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/mdx-DEWtB1K5",["exports","./editor.api-CalNCsUg"],(function(t,e){"use strict";const o={comments:{blockComment:["{/*","*/}"]},brackets:[["{","}"]],autoClosingPairs:[{open:'"',close:'"'},{open:"'",close:"'"},{open:"“",close:"”"},{open:"‘",close:"’"},{open:"`",close:"`"},{open:"{",close:"}"},{open:"(",close:")"},{open:"_",close:"_"},{open:"**",close:"**"},{open:"<",close:">"}],onEnterRules:[{beforeText:/^\s*- .+/,action:{indentAction:e.languages.IndentAction.None,appendText:"- "}},{beforeText:/^\s*\+ .+/,action:{indentAction:e.languages.IndentAction.None,appendText:"+ "}},{beforeText:/^\s*\* .+/,action:{indentAction:e.languages.IndentAction.None,appendText:"* "}},{beforeText:/^> /,action:{indentAction:e.languages.IndentAction.None,appendText:"> "}},{beforeText:/<\w+/,action:{indentAction:e.languages.IndentAction.Indent}},{beforeText:/\s+>\s*$/,action:{indentAction:e.languages.IndentAction.Indent}},{beforeText:/<\/\w+>/,action:{indentAction:e.languages.IndentAction.Outdent}},...Array.from({length:100},(s,n)=>({beforeText:new RegExp(`^${n}\\. .+`),action:{indentAction:e.languages.IndentAction.None,appendText:`${n+1}. `}}))]},i={defaultToken:"",tokenPostfix:".mdx",control:/[!#()*+.[\\\]_`{}\-]/,escapes:/\\@control/,tokenizer:{root:[[/^---$/,{token:"meta.content",next:"@frontmatter",nextEmbedded:"yaml"}],[/^\s*import/,{token:"keyword",next:"@import",nextEmbedded:"js"}],[/^\s*export/,{token:"keyword",next:"@export",nextEmbedded:"js"}],[/<\w+/,{token:"type.identifier",next:"@jsx"}],[/<\/?\w+>/,"type.identifier"],[/^(\s*)(>*\s*)(#{1,6}\s)/,[{token:"white"},{token:"comment"},{token:"keyword",next:"@header"}]],[/^(\s*)(>*\s*)([*+-])(\s+)/,["white","comment","keyword","white"]],[/^(\s*)(>*\s*)(\d{1,9}\.)(\s+)/,["white","comment","number","white"]],[/^(\s*)(>*\s*)(\d{1,9}\.)(\s+)/,["white","comment","number","white"]],[/^(\s*)(>*\s*)(-{3,}|\*{3,}|_{3,})$/,["white","comment","keyword"]],[/`{3,}(\s.*)?$/,{token:"string",next:"@codeblock_backtick"}],[/~{3,}(\s.*)?$/,{token:"string",next:"@codeblock_tilde"}],[/`{3,}(\S+).*$/,{token:"string",next:"@codeblock_highlight_backtick",nextEmbedded:"$1"}],[/~{3,}(\S+).*$/,{token:"string",next:"@codeblock_highlight_tilde",nextEmbedded:"$1"}],[/^(\s*)(-{4,})$/,["white","comment"]],[/^(\s*)(>+)/,["white","comment"]],{include:"content"}],content:[[/(\[)(.+)(]\()(.+)(\s+".*")(\))/,["","string.link","","type.identifier","string.link",""]],[/(\[)(.+)(]\()(.+)(\))/,["","type.identifier","","string.link",""]],[/(\[)(.+)(]\[)(.+)(])/,["","type.identifier","","type.identifier",""]],[/(\[)(.+)(]:\s+)(\S*)/,["","type.identifier","","string.link"]],[/(\[)(.+)(])/,["","type.identifier",""]],[/`.*`/,"variable.source"],[/_/,{token:"emphasis",next:"@emphasis_underscore"}],[/\*(?!\*)/,{token:"emphasis",next:"@emphasis_asterisk"}],[/\*\*/,{token:"strong",next:"@strong"}],[/{/,{token:"delimiter.bracket",next:"@expression",nextEmbedded:"js"}]],import:[[/'\s*(;|$)/,{token:"string",next:"@pop",nextEmbedded:"@pop"}]],expression:[[/{/,{token:"delimiter.bracket",next:"@expression"}],[/}/,{token:"delimiter.bracket",next:"@pop",nextEmbedded:"@pop"}]],export:[[/^\s*$/,{token:"delimiter.bracket",next:"@pop",nextEmbedded:"@pop"}]],jsx:[[/\s+/,""],[/(\w+)(=)("(?:[^"\\]|\\.)*")/,["attribute.name","operator","string"]],[/(\w+)(=)('(?:[^'\\]|\\.)*')/,["attribute.name","operator","string"]],[/(\w+(?=\s|>|={|$))/,["attribute.name"]],[/={/,{token:"delimiter.bracket",next:"@expression",nextEmbedded:"js"}],[/>/,{token:"type.identifier",next:"@pop"}]],header:[[/.$/,{token:"keyword",next:"@pop"}],{include:"content"},[/./,{token:"keyword"}]],strong:[[/\*\*/,{token:"strong",next:"@pop"}],{include:"content"},[/./,{token:"strong"}]],emphasis_underscore:[[/_/,{token:"emphasis",next:"@pop"}],{include:"content"},[/./,{token:"emphasis"}]],emphasis_asterisk:[[/\*(?!\*)/,{token:"emphasis",next:"@pop"}],{include:"content"},[/./,{token:"emphasis"}]],frontmatter:[[/^---$/,{token:"meta.content",nextEmbedded:"@pop",next:"@pop"}]],codeblock_highlight_backtick:[[/\s*`{3,}\s*$/,{token:"string",next:"@pop",nextEmbedded:"@pop"}],[/.*$/,"variable.source"]],codeblock_highlight_tilde:[[/\s*~{3,}\s*$/,{token:"string",next:"@pop",nextEmbedded:"@pop"}],[/.*$/,"variable.source"]],codeblock_backtick:[[/\s*`{3,}\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]],codeblock_tilde:[[/\s*~{3,}\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]]}};t.conf=o,t.language=i,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/mips-CiYP61RB",["exports"],(function(e){"use strict";const t={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#%\^\&\*\(\)\=\$\-\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{blockComment:["###","###"],lineComment:"#"},folding:{markers:{start:new RegExp("^\\s*#region\\b"),end:new RegExp("^\\s*#endregion\\b")}}},n={defaultToken:"",ignoreCase:!1,tokenPostfix:".mips",regEx:/\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,keywords:[".data",".text","syscall","trap","add","addu","addi","addiu","and","andi","div","divu","mult","multu","nor","or","ori","sll","slv","sra","srav","srl","srlv","sub","subu","xor","xori","lhi","lho","lhi","llo","slt","slti","sltu","sltiu","beq","bgtz","blez","bne","j","jal","jalr","jr","lb","lbu","lh","lhu","lw","li","la","sb","sh","sw","mfhi","mflo","mthi","mtlo","move"],symbols:/[\.,\:]+/,escapes:/\\(?:[abfnrtv\\"'$]|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/\$[a-zA-Z_]\w*/,"variable.predefined"],[/[.a-zA-Z_]\w*/,{cases:{this:"variable.predefined","@keywords":{token:"keyword.$0"},"@default":""}}],[/[ \t\r\n]+/,""],[/#.*$/,"comment"],["///",{token:"regexp",next:"@hereregexp"}],[/^(\s*)(@regEx)/,["","regexp"]],[/(\,)(\s*)(@regEx)/,["delimiter","","regexp"]],[/(\:)(\s*)(@regEx)/,["delimiter","","regexp"]],[/@symbols/,"delimiter"],[/\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d+\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F]+/,"number.hex"],[/0[0-7]+(?!\d)/,"number.octal"],[/\d+/,"number"],[/[,.]/,"delimiter"],[/"""/,"string",'@herestring."""'],[/'''/,"string","@herestring.'''"],[/"/,{cases:{"@eos":"string","@default":{token:"string",next:'@string."'}}}],[/'/,{cases:{"@eos":"string","@default":{token:"string",next:"@string.'"}}}]],string:[[/[^"'\#\\]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/\./,"string.escape.invalid"],[/#{/,{cases:{'$S2=="':{token:"string",next:"root.interpolatedstring"},"@default":"string"}}],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/#/,"string"]],herestring:[[/("""|''')/,{cases:{"$1==$S2":{token:"string",next:"@pop"},"@default":"string"}}],[/[^#\\'"]+/,"string"],[/['"]+/,"string"],[/@escapes/,"string.escape"],[/\./,"string.escape.invalid"],[/#{/,{token:"string.quote",next:"root.interpolatedstring"}],[/#/,"string"]],comment:[[/[^#]+/,"comment"],[/#/,"comment"]],hereregexp:[[/[^\\\/#]+/,"regexp"],[/\\./,"regexp"],[/#.*$/,"comment"],["///[igm]*",{token:"regexp",next:"@pop"}],[/\//,"regexp"]]}};e.conf=t,e.language=n,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/monaco.contribution-D2OdxNBt",["require","exports","./editor.api-CalNCsUg"],(function(S,n,a){"use strict";const O="5.9.3";var u=(e=>(e[e.None=0]="None",e[e.CommonJS=1]="CommonJS",e[e.AMD=2]="AMD",e[e.UMD=3]="UMD",e[e.System=4]="System",e[e.ES2015=5]="ES2015",e[e.ESNext=99]="ESNext",e))(u||{}),c=(e=>(e[e.None=0]="None",e[e.Preserve=1]="Preserve",e[e.React=2]="React",e[e.ReactNative=3]="ReactNative",e[e.ReactJSX=4]="ReactJSX",e[e.ReactJSXDev=5]="ReactJSXDev",e))(c||{}),l=(e=>(e[e.CarriageReturnLineFeed=0]="CarriageReturnLineFeed",e[e.LineFeed=1]="LineFeed",e))(l||{}),g=(e=>(e[e.ES3=0]="ES3",e[e.ES5=1]="ES5",e[e.ES2015=2]="ES2015",e[e.ES2016=3]="ES2016",e[e.ES2017=4]="ES2017",e[e.ES2018=5]="ES2018",e[e.ES2019=6]="ES2019",e[e.ES2020=7]="ES2020",e[e.ESNext=99]="ESNext",e[e.JSON=100]="JSON",e[e.Latest=99]="Latest",e))(g||{}),b=(e=>(e[e.Classic=1]="Classic",e[e.NodeJs=2]="NodeJs",e))(b||{});class d{constructor(t,s,i,r,o){this._onDidChange=new a.Emitter,this._onDidExtraLibsChange=new a.Emitter,this._extraLibs=Object.create(null),this._removedExtraLibs=Object.create(null),this._eagerModelSync=!1,this.setCompilerOptions(t),this.setDiagnosticsOptions(s),this.setWorkerOptions(i),this.setInlayHintsOptions(r),this.setModeConfiguration(o),this._onDidExtraLibsChangeTimeout=-1}get onDidChange(){return this._onDidChange.event}get onDidExtraLibsChange(){return this._onDidExtraLibsChange.event}get modeConfiguration(){return this._modeConfiguration}get workerOptions(){return this._workerOptions}get inlayHintsOptions(){return this._inlayHintsOptions}getExtraLibs(){return this._extraLibs}addExtraLib(t,s){let i;if(typeof s>"u"?i=`ts:extralib-${Math.random().toString(36).substring(2,15)}`:i=s,this._extraLibs[i]&&this._extraLibs[i].content===t)return{dispose:()=>{}};let r=1;return this._removedExtraLibs[i]&&(r=this._removedExtraLibs[i]+1),this._extraLibs[i]&&(r=this._extraLibs[i].version+1),this._extraLibs[i]={content:t,version:r},this._fireOnDidExtraLibsChangeSoon(),{dispose:()=>{let o=this._extraLibs[i];o&&o.version===r&&(delete this._extraLibs[i],this._removedExtraLibs[i]=r,this._fireOnDidExtraLibsChangeSoon())}}}setExtraLibs(t){for(const s in this._extraLibs)this._removedExtraLibs[s]=this._extraLibs[s].version;if(this._extraLibs=Object.create(null),t&&t.length>0)for(const s of t){const i=s.filePath||`ts:extralib-${Math.random().toString(36).substring(2,15)}`,r=s.content;let o=1;this._removedExtraLibs[i]&&(o=this._removedExtraLibs[i]+1),this._extraLibs[i]={content:r,version:o}}this._fireOnDidExtraLibsChangeSoon()}_fireOnDidExtraLibsChangeSoon(){this._onDidExtraLibsChangeTimeout===-1&&(this._onDidExtraLibsChangeTimeout=window.setTimeout(()=>{this._onDidExtraLibsChangeTimeout=-1,this._onDidExtraLibsChange.fire(void 0)},0))}getCompilerOptions(){return this._compilerOptions}setCompilerOptions(t){this._compilerOptions=t||Object.create(null),this._onDidChange.fire(void 0)}getDiagnosticsOptions(){return this._diagnosticsOptions}setDiagnosticsOptions(t){this._diagnosticsOptions=t||Object.create(null),this._onDidChange.fire(void 0)}setWorkerOptions(t){this._workerOptions=t||Object.create(null),this._onDidChange.fire(void 0)}setInlayHintsOptions(t){this._inlayHintsOptions=t||Object.create(null),this._onDidChange.fire(void 0)}setMaximumWorkerIdleTime(t){}setEagerModelSync(t){this._eagerModelSync=t}getEagerModelSync(){return this._eagerModelSync}setModeConfiguration(t){this._modeConfiguration=t||Object.create(null),this._onDidChange.fire(void 0)}}const m=O,v={completionItems:!0,hovers:!0,documentSymbols:!0,definitions:!0,references:!0,documentHighlights:!0,rename:!0,diagnostics:!0,documentRangeFormattingEdits:!0,signatureHelp:!0,onTypeFormattingEdits:!0,codeActions:!0,inlayHints:!0},_=new d({allowNonTsExtensions:!0,target:99},{noSemanticValidation:!1,noSyntaxValidation:!1,onlyVisible:!1},{},{},v),f=new d({allowNonTsExtensions:!0,allowJs:!0,target:99},{noSemanticValidation:!0,noSyntaxValidation:!1,onlyVisible:!1},{},{},v),L=()=>h().then(e=>e.getTypeScriptWorker()),E=()=>h().then(e=>e.getJavaScriptWorker());function h(){return new Promise((e,t)=>S(["./tsMode-CZz1Umrk"],e,t))}a.languages.onLanguage("typescript",()=>h().then(e=>e.setupTypeScript(_))),a.languages.onLanguage("javascript",()=>h().then(e=>e.setupJavaScript(f)));const C=Object.freeze(Object.defineProperty({__proto__:null,JsxEmit:c,ModuleKind:u,ModuleResolutionKind:b,NewLineKind:l,ScriptTarget:g,getJavaScriptWorker:E,getTypeScriptWorker:L,javascriptDefaults:f,typescriptDefaults:_,typescriptVersion:m},Symbol.toStringTag,{value:"Module"}));n.JsxEmit=c,n.ModuleKind=u,n.ModuleResolutionKind=b,n.NewLineKind=l,n.ScriptTarget=g,n.getJavaScriptWorker=E,n.getTypeScriptWorker=L,n.javascriptDefaults=f,n.monaco_contribution=C,n.typescriptDefaults=_,n.typescriptVersion=m}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/monaco.contribution-DO3azKX8",["require","exports","./editor.api-CalNCsUg"],(function(l,n,o){"use strict";class s{constructor(e,f,p){this._onDidChange=new o.Emitter,this._languageId=e,this.setOptions(f),this.setModeConfiguration(p)}get onDidChange(){return this._onDidChange.event}get languageId(){return this._languageId}get modeConfiguration(){return this._modeConfiguration}get diagnosticsOptions(){return this.options}get options(){return this._options}setOptions(e){this._options=e||Object.create(null),this._onDidChange.fire(this)}setDiagnosticsOptions(e){this.setOptions(e)}setModeConfiguration(e){this._modeConfiguration=e||Object.create(null),this._onDidChange.fire(this)}}const r={validate:!0,lint:{compatibleVendorPrefixes:"ignore",vendorPrefix:"warning",duplicateProperties:"warning",emptyRules:"warning",importStatement:"ignore",boxModel:"ignore",universalSelector:"ignore",zeroUnits:"ignore",fontFaceProperties:"warning",hexColorLength:"error",argumentsInColorFunction:"error",unknownProperties:"warning",ieHack:"ignore",unknownVendorSpecificProperties:"ignore",propertyIgnoredDueToDisplay:"warning",important:"ignore",float:"ignore",idSelector:"ignore"},data:{useDefaultDataProvider:!0},format:{newlineBetweenSelectors:!0,newlineBetweenRules:!0,spaceAroundSelectorSeparator:!1,braceStyle:"collapse",maxPreserveNewLines:void 0,preserveNewLines:!0}},i={completionItems:!0,hovers:!0,documentSymbols:!0,definitions:!0,references:!0,documentHighlights:!0,rename:!0,colors:!0,foldingRanges:!0,diagnostics:!0,selectionRanges:!0,documentFormattingEdits:!0,documentRangeFormattingEdits:!0},a=new s("css",r,i),u=new s("scss",r,i),g=new s("less",r,i);function c(){return new Promise((t,e)=>l(["./cssMode-CjiAH6dQ"],t,e))}o.languages.onLanguage("less",()=>{c().then(t=>t.setupMode(g))}),o.languages.onLanguage("scss",()=>{c().then(t=>t.setupMode(u))}),o.languages.onLanguage("css",()=>{c().then(t=>t.setupMode(a))});const d=Object.freeze(Object.defineProperty({__proto__:null,cssDefaults:a,lessDefaults:g,scssDefaults:u},Symbol.toStringTag,{value:"Module"}));n.cssDefaults=a,n.lessDefaults=g,n.monaco_contribution=d,n.scssDefaults=u}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/monaco.contribution-EcChJV6a",["require","exports","./editor.api-CalNCsUg"],(function(a,n,o){"use strict";class u{constructor(e,d,m){this._onDidChange=new o.Emitter,this._languageId=e,this.setDiagnosticsOptions(d),this.setModeConfiguration(m)}get onDidChange(){return this._onDidChange.event}get languageId(){return this._languageId}get modeConfiguration(){return this._modeConfiguration}get diagnosticsOptions(){return this._diagnosticsOptions}setDiagnosticsOptions(e){this._diagnosticsOptions=e||Object.create(null),this._onDidChange.fire(this)}setModeConfiguration(e){this._modeConfiguration=e||Object.create(null),this._onDidChange.fire(this)}}const g={validate:!0,allowComments:!0,schemas:[],enableSchemaRequest:!1,schemaRequest:"warning",schemaValidation:"warning",comments:"error",trailingCommas:"error"},c={documentFormattingEdits:!0,documentRangeFormattingEdits:!0,completionItems:!0,hovers:!0,documentSymbols:!0,tokens:!0,colors:!0,foldingRanges:!0,diagnostics:!0,selectionRanges:!0},s=new u("json",g,c),i=()=>r().then(t=>t.getWorker());function r(){return new Promise((t,e)=>a(["./jsonMode-DULH5oaX"],t,e))}o.languages.register({id:"json",extensions:[".json",".bowerrc",".jshintrc",".jscsrc",".eslintrc",".babelrc",".har"],aliases:["JSON","json"],mimetypes:["application/json"]}),o.languages.onLanguage("json",()=>{r().then(t=>t.setupMode(s))});const l=Object.freeze(Object.defineProperty({__proto__:null,getWorker:i,jsonDefaults:s},Symbol.toStringTag,{value:"Module"}));n.getWorker=i,n.jsonDefaults=s,n.monaco_contribution=l}));
|
||||
+1
@@ -0,0 +1 @@
|
||||
define("vs/monaco.contribution-qLAYrEOP",["require","exports","./editor.api-CalNCsUg"],(function(p,n,f){"use strict";class D{constructor(t,c,s){this._onDidChange=new f.Emitter,this._languageId=t,this.setOptions(c),this.setModeConfiguration(s)}get onDidChange(){return this._onDidChange.event}get languageId(){return this._languageId}get options(){return this._options}get modeConfiguration(){return this._modeConfiguration}setOptions(t){this._options=t||Object.create(null),this._onDidChange.fire(this)}setModeConfiguration(t){this._modeConfiguration=t||Object.create(null),this._onDidChange.fire(this)}}const r={format:{tabSize:4,insertSpaces:!1,wrapLineLength:120,unformatted:'default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var',contentUnformatted:"pre",indentInnerHtml:!1,preserveNewLines:!0,maxPreserveNewLines:void 0,indentHandlebars:!1,endWithNewline:!1,extraLiners:"head, body, /html",wrapAttributes:"auto"},suggest:{},data:{useDefaultDataProvider:!0}};function i(e){return{completionItems:!0,hovers:!0,documentSymbols:!0,links:!0,documentHighlights:!0,rename:!0,colors:!0,foldingRanges:!0,selectionRanges:!0,diagnostics:e===a,documentFormattingEdits:e===a,documentRangeFormattingEdits:e===a}}const a="html",m="handlebars",h="razor",u=o(a,r,i(a)),b=u.defaults,g=o(m,r,i(m)),L=g.defaults,l=o(h,r,i(h)),v=l.defaults;function _(){return new Promise((e,t)=>p(["./htmlMode-Bz67EXwp"],e,t))}function o(e,t=r,c=i(e)){const s=new D(e,t,c);let d;const C=f.languages.onLanguage(e,async()=>{d=(await _()).setupMode(s)});return{defaults:s,dispose(){C.dispose(),d?.dispose(),d=void 0}}}const S=Object.freeze(Object.defineProperty({__proto__:null,handlebarDefaults:L,handlebarLanguageService:g,htmlDefaults:b,htmlLanguageService:u,razorDefaults:v,razorLanguageService:l,registerHTMLLanguageService:o},Symbol.toStringTag,{value:"Module"}));n.handlebarDefaults=L,n.handlebarLanguageService=g,n.htmlDefaults=b,n.htmlLanguageService=u,n.monaco_contribution=S,n.razorDefaults=v,n.razorLanguageService=l,n.registerHTMLLanguageService=o}));
|
||||
@@ -0,0 +1 @@
|
||||
define("vs/msdax-C38-sJlp",["exports"],(function(E){"use strict";const T={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["[","]"],["(",")"],["{","}"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:"{",close:"}",notIn:["string","comment"]}]},A={defaultToken:"",tokenPostfix:".msdax",ignoreCase:!0,brackets:[{open:"[",close:"]",token:"delimiter.square"},{open:"{",close:"}",token:"delimiter.brackets"},{open:"(",close:")",token:"delimiter.parenthesis"}],keywords:["VAR","RETURN","NOT","EVALUATE","DATATABLE","ORDER","BY","START","AT","DEFINE","MEASURE","ASC","DESC","IN","BOOLEAN","DOUBLE","INTEGER","DATETIME","CURRENCY","STRING"],functions:["CLOSINGBALANCEMONTH","CLOSINGBALANCEQUARTER","CLOSINGBALANCEYEAR","DATEADD","DATESBETWEEN","DATESINPERIOD","DATESMTD","DATESQTD","DATESYTD","ENDOFMONTH","ENDOFQUARTER","ENDOFYEAR","FIRSTDATE","FIRSTNONBLANK","LASTDATE","LASTNONBLANK","NEXTDAY","NEXTMONTH","NEXTQUARTER","NEXTYEAR","OPENINGBALANCEMONTH","OPENINGBALANCEQUARTER","OPENINGBALANCEYEAR","PARALLELPERIOD","PREVIOUSDAY","PREVIOUSMONTH","PREVIOUSQUARTER","PREVIOUSYEAR","SAMEPERIODLASTYEAR","STARTOFMONTH","STARTOFQUARTER","STARTOFYEAR","TOTALMTD","TOTALQTD","TOTALYTD","ADDCOLUMNS","ADDMISSINGITEMS","ALL","ALLEXCEPT","ALLNOBLANKROW","ALLSELECTED","CALCULATE","CALCULATETABLE","CALENDAR","CALENDARAUTO","CROSSFILTER","CROSSJOIN","CURRENTGROUP","DATATABLE","DETAILROWS","DISTINCT","EARLIER","EARLIEST","EXCEPT","FILTER","FILTERS","GENERATE","GENERATEALL","GROUPBY","IGNORE","INTERSECT","ISONORAFTER","KEEPFILTERS","LOOKUPVALUE","NATURALINNERJOIN","NATURALLEFTOUTERJOIN","RELATED","RELATEDTABLE","ROLLUP","ROLLUPADDISSUBTOTAL","ROLLUPGROUP","ROLLUPISSUBTOTAL","ROW","SAMPLE","SELECTCOLUMNS","SUBSTITUTEWITHINDEX","SUMMARIZE","SUMMARIZECOLUMNS","TOPN","TREATAS","UNION","USERELATIONSHIP","VALUES","SUM","SUMX","PATH","PATHCONTAINS","PATHITEM","PATHITEMREVERSE","PATHLENGTH","AVERAGE","AVERAGEA","AVERAGEX","COUNT","COUNTA","COUNTAX","COUNTBLANK","COUNTROWS","COUNTX","DISTINCTCOUNT","DIVIDE","GEOMEAN","GEOMEANX","MAX","MAXA","MAXX","MEDIAN","MEDIANX","MIN","MINA","MINX","PERCENTILE.EXC","PERCENTILE.INC","PERCENTILEX.EXC","PERCENTILEX.INC","PRODUCT","PRODUCTX","RANK.EQ","RANKX","STDEV.P","STDEV.S","STDEVX.P","STDEVX.S","VAR.P","VAR.S","VARX.P","VARX.S","XIRR","XNPV","DATE","DATEDIFF","DATEVALUE","DAY","EDATE","EOMONTH","HOUR","MINUTE","MONTH","NOW","SECOND","TIME","TIMEVALUE","TODAY","WEEKDAY","WEEKNUM","YEAR","YEARFRAC","CONTAINS","CONTAINSROW","CUSTOMDATA","ERROR","HASONEFILTER","HASONEVALUE","ISBLANK","ISCROSSFILTERED","ISEMPTY","ISERROR","ISEVEN","ISFILTERED","ISLOGICAL","ISNONTEXT","ISNUMBER","ISODD","ISSUBTOTAL","ISTEXT","USERNAME","USERPRINCIPALNAME","AND","FALSE","IF","IFERROR","NOT","OR","SWITCH","TRUE","ABS","ACOS","ACOSH","ACOT","ACOTH","ASIN","ASINH","ATAN","ATANH","BETA.DIST","BETA.INV","CEILING","CHISQ.DIST","CHISQ.DIST.RT","CHISQ.INV","CHISQ.INV.RT","COMBIN","COMBINA","CONFIDENCE.NORM","CONFIDENCE.T","COS","COSH","COT","COTH","CURRENCY","DEGREES","EVEN","EXP","EXPON.DIST","FACT","FLOOR","GCD","INT","ISO.CEILING","LCM","LN","LOG","LOG10","MOD","MROUND","ODD","PERMUT","PI","POISSON.DIST","POWER","QUOTIENT","RADIANS","RAND","RANDBETWEEN","ROUND","ROUNDDOWN","ROUNDUP","SIGN","SIN","SINH","SQRT","SQRTPI","TAN","TANH","TRUNC","BLANK","CONCATENATE","CONCATENATEX","EXACT","FIND","FIXED","FORMAT","LEFT","LEN","LOWER","MID","REPLACE","REPT","RIGHT","SEARCH","SUBSTITUTE","TRIM","UNICHAR","UNICODE","UPPER","VALUE"],tokenizer:{root:[{include:"@comments"},{include:"@whitespace"},{include:"@numbers"},{include:"@strings"},{include:"@complexIdentifiers"},[/[;,.]/,"delimiter"],[/[({})]/,"@brackets"],[/[a-z_][a-zA-Z0-9_]*/,{cases:{"@keywords":"keyword","@functions":"keyword","@default":"identifier"}}],[/[<>=!%&+\-*/|~^]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[[/\/\/+.*/,"comment"],[/\/\*/,{token:"comment.quote",next:"@comment"}]],comment:[[/[^*/]+/,"comment"],[/\*\//,{token:"comment.quote",next:"@pop"}],[/./,"comment"]],numbers:[[/0[xX][0-9a-fA-F]*/,"number"],[/[$][+-]*\d*(\.\d*)?/,"number"],[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/,"number"]],strings:[[/N"/,{token:"string",next:"@string"}],[/"/,{token:"string",next:"@string"}]],string:[[/[^"]+/,"string"],[/""/,"string"],[/"/,{token:"string",next:"@pop"}]],complexIdentifiers:[[/\[/,{token:"identifier.quote",next:"@bracketedIdentifier"}],[/'/,{token:"identifier.quote",next:"@quotedIdentifier"}]],bracketedIdentifier:[[/[^\]]+/,"identifier"],[/]]/,"identifier"],[/]/,{token:"identifier.quote",next:"@pop"}]],quotedIdentifier:[[/[^']+/,"identifier"],[/''/,"identifier"],[/'/,{token:"identifier.quote",next:"@pop"}]]}};E.conf=T,E.language=A,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/nls.messages-loader",["exports"],(function(s){"use strict";function a(o,l,n,t){const e=t["vs/nls"]?.availableLanguages?.["*"];!e||e==="en"?n({}):l([`vs/nls.messages.${e}`],()=>{n({})})}s.load=a,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
define("vs/objective-c-CntZFaHX",["exports"],(function(e){"use strict";const n={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},o={defaultToken:"",tokenPostfix:".objective-c",keywords:["#import","#include","#define","#else","#endif","#if","#ifdef","#ifndef","#ident","#undef","@class","@defs","@dynamic","@encode","@end","@implementation","@interface","@package","@private","@protected","@property","@protocol","@public","@selector","@synthesize","__declspec","assign","auto","BOOL","break","bycopy","byref","case","char","Class","const","copy","continue","default","do","double","else","enum","extern","FALSE","false","float","for","goto","if","in","int","id","inout","IMP","long","nil","nonatomic","NULL","oneway","out","private","public","protected","readwrite","readonly","register","return","SEL","self","short","signed","sizeof","static","struct","super","switch","typedef","TRUE","true","union","unsigned","volatile","void","while"],decpart:/\d(_?\d)*/,decimal:/0|@decpart/,tokenizer:{root:[{include:"@comments"},{include:"@whitespace"},{include:"@numbers"},{include:"@strings"},[/[,:;]/,"delimiter"],[/[{}\[\]()<>]/,"@brackets"],[/[a-zA-Z@#]\w*/,{cases:{"@keywords":"keyword","@default":"identifier"}}],[/[<>=\\+\\-\\*\\/\\^\\|\\~,]|and\\b|or\\b|not\\b]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[[/0[xX][0-9a-fA-F]*(_?[0-9a-fA-F])*/,"number.hex"],[/@decimal((\.@decpart)?([eE][\-+]?@decpart)?)[fF]*/,{cases:{"(\\d)*":"number",$0:"number.float"}}]],strings:[[/'$/,"string.escape","@popall"],[/'/,"string.escape","@stringBody"],[/"$/,"string.escape","@popall"],[/"/,"string.escape","@dblStringBody"]],stringBody:[[/[^\\']+$/,"string","@popall"],[/[^\\']+/,"string"],[/\\./,"string"],[/'/,"string.escape","@popall"],[/\\$/,"string"]],dblStringBody:[[/[^\\"]+$/,"string","@popall"],[/[^\\"]+/,"string"],[/\\./,"string"],[/"/,"string.escape","@popall"],[/\\$/,"string"]]}};e.conf=n,e.language=o,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})}));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user