@cratis/screenplay-language 4.53.0 → 4.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,21 @@ monaco.editor.create(element, {
31
31
  - **Hover** — keyword documentation, concept definitions (primitive + attributes), policy require expressions, and event property lists.
32
32
  - **Diagnostics** — unknown slice types, unknown primitive types, references to undeclared policies and events, tab indentation, and unclosed code fences. Every one of them carries the compiler's own `PLAY` code (`diagnosticCodes`), so a squiggle and a CLI diagnostic for the same condition are the same code.
33
33
 
34
+ ## Quick fixes
35
+
36
+ The registered code-action provider offers verified edits against the current buffer:
37
+
38
+ - `PLAY0479`: replace one legacy `?` suffix with `optional`, or migrate the document.
39
+ - `PLAY0471`: remove a redundant event `id` line. Trailing comments prevent removal.
40
+
41
+ Redundant id removal works in placed and multi-document applications.
42
+ Actions respect the requested diagnostic and kind, cache analysis per model version,
43
+ and pin edits to that version. Range requests return every eligible intersecting occurrence, without duplicate
44
+ actions. Independent recipes are verified together once per version to check the intended
45
+ syntax changes and diagnostic removal without reparsing per marker; no .NET process is
46
+ required. See [editor quick fixes](../../../../Documentation/screenplay/vscode.md#event-quick-fixes)
47
+ for the redundant id removal behavior.
48
+
34
49
  ## Extending with new sub-languages
35
50
 
36
51
  ```typescript
@@ -941,6 +941,48 @@ var DiagnosticCodes = {
941
941
  InvalidTriggerData: "PLAY0246"
942
942
  };
943
943
 
944
+ // ../../Compiler/dist/Parsing/SourceLineSplitter.js
945
+ function splitLines(source, hashComments = false, path) {
946
+ const result = [];
947
+ let number = 0;
948
+ let offset = 0;
949
+ for (const raw of source.split("\n")) {
950
+ number++;
951
+ const line = raw.endsWith("\r") ? raw.replace(/\r+$/, "") : raw;
952
+ const indent = line.length - line.trimStart().length;
953
+ const content = stripComment(line.substring(indent), hashComments).trimEnd();
954
+ result.push(path === void 0 ? { number, raw: line, indent, content, startOffset: offset } : { number, raw: line, indent, content, path, startOffset: offset });
955
+ offset += raw.length + 1;
956
+ }
957
+ return result;
958
+ }
959
+ function commentStart(text, hashComments = false) {
960
+ let inString = false;
961
+ let inTemplate = false;
962
+ for (let index = 0; index < text.length; index++) {
963
+ const current = text[index];
964
+ if (current === "\\" && inString && index + 1 < text.length) {
965
+ index++;
966
+ } else if (current === '"' && !inTemplate) {
967
+ inString = !inString;
968
+ } else if (current === "`" && !inString) {
969
+ inTemplate = !inTemplate;
970
+ } else if (!inString && !inTemplate) {
971
+ if (current === "/" && index + 1 < text.length && text[index + 1] === "/") {
972
+ return index;
973
+ }
974
+ if (hashComments && current === "#") {
975
+ return index;
976
+ }
977
+ }
978
+ }
979
+ return -1;
980
+ }
981
+ function stripComment(text, hashComments) {
982
+ const comment = commentStart(text, hashComments);
983
+ return comment < 0 ? text : text.substring(0, comment);
984
+ }
985
+
944
986
  // ../../Compiler/dist/Files/PlayPlacement.js
945
987
  var documentPlacement = [];
946
988
  var isDocumentPlacement = (placement) => placement.length === 0;
@@ -3824,48 +3866,6 @@ function parsePersona(context, line) {
3824
3866
  return { kind: "PersonaSyntax", name, description, policies, location: locationOf(line) };
3825
3867
  }
3826
3868
 
3827
- // ../../Compiler/dist/Parsing/SourceLineSplitter.js
3828
- function splitLines(source, hashComments = false, path) {
3829
- const result = [];
3830
- let number = 0;
3831
- let offset = 0;
3832
- for (const raw of source.split("\n")) {
3833
- number++;
3834
- const line = raw.endsWith("\r") ? raw.replace(/\r+$/, "") : raw;
3835
- const indent = line.length - line.trimStart().length;
3836
- const content = stripComment(line.substring(indent), hashComments).trimEnd();
3837
- result.push(path === void 0 ? { number, raw: line, indent, content, startOffset: offset } : { number, raw: line, indent, content, path, startOffset: offset });
3838
- offset += raw.length + 1;
3839
- }
3840
- return result;
3841
- }
3842
- function commentStart(text, hashComments = false) {
3843
- let inString = false;
3844
- let inTemplate = false;
3845
- for (let index = 0; index < text.length; index++) {
3846
- const current = text[index];
3847
- if (current === "\\" && inString && index + 1 < text.length) {
3848
- index++;
3849
- } else if (current === '"' && !inTemplate) {
3850
- inString = !inString;
3851
- } else if (current === "`" && !inString) {
3852
- inTemplate = !inTemplate;
3853
- } else if (!inString && !inTemplate) {
3854
- if (current === "/" && index + 1 < text.length && text[index + 1] === "/") {
3855
- return index;
3856
- }
3857
- if (hashComments && current === "#") {
3858
- return index;
3859
- }
3860
- }
3861
- }
3862
- return -1;
3863
- }
3864
- function stripComment(text, hashComments) {
3865
- const comment = commentStart(text, hashComments);
3866
- return comment < 0 ? text : text.substring(0, comment);
3867
- }
3868
-
3869
3869
  // ../../Compiler/dist/ScreenplayCompiler.js
3870
3870
  function parse(source, path, placement = documentPlacement) {
3871
3871
  const { value, diagnostics, success } = parseForAuthoring(source, path, placement);
@@ -3884,33 +3884,342 @@ function parseForAuthoring(source, path, placement = documentPlacement) {
3884
3884
  };
3885
3885
  }
3886
3886
 
3887
- // ../../Compiler/dist/Syntax/SyntaxJson.js
3888
- var isNode = (value) => typeof value === "object" && value !== null && typeof value.kind === "string";
3889
- function toSyntaxJson(node) {
3890
- return write(node);
3891
- }
3892
- function write(value) {
3893
- if (Array.isArray(value)) {
3894
- return value.map(write);
3887
+ // ../../Compiler/dist/Syntax/ScreenplaySyntaxWalker.js
3888
+ var ScreenplaySyntaxWalker = class {
3889
+ visitNode(_node) {
3890
+ }
3891
+ visitApplication(syntax) {
3892
+ this.visitNode(syntax);
3893
+ if (syntax.domain !== null)
3894
+ this.visitDomain(syntax.domain);
3895
+ syntax.imports.forEach((node) => this.visitImport(node));
3896
+ syntax.fileImports.forEach((node) => this.visitFileImport(node));
3897
+ syntax.concepts.forEach((node) => this.visitConcept(node));
3898
+ syntax.types.forEach((node) => this.visitType(node));
3899
+ syntax.personas.forEach((node) => this.visitPersona(node));
3900
+ syntax.modules.forEach((node) => this.visitModule(node));
3901
+ }
3902
+ visitPersona(syntax) {
3903
+ this.visitNode(syntax);
3904
+ }
3905
+ visitAuthorize(syntax) {
3906
+ this.visitNode(syntax);
3907
+ this.visitPolicyRequirement(syntax.requirement);
3908
+ }
3909
+ visitPolicyRequirement(syntax) {
3910
+ this.visitNode(syntax);
3911
+ if (syntax.kind === "LogicalPolicyRequirementSyntax") {
3912
+ this.visitPolicyRequirement(syntax.left);
3913
+ this.visitPolicyRequirement(syntax.right);
3914
+ }
3915
+ }
3916
+ visitDomain(syntax) {
3917
+ this.visitNode(syntax);
3918
+ }
3919
+ visitImport(syntax) {
3920
+ this.visitNode(syntax);
3921
+ }
3922
+ visitFileImport(syntax) {
3923
+ this.visitNode(syntax);
3924
+ }
3925
+ visitConcept(syntax) {
3926
+ this.visitNode(syntax);
3927
+ syntax.attributes.forEach((node) => this.visitNode(node));
3928
+ }
3929
+ visitType(syntax) {
3930
+ this.visitNode(syntax);
3931
+ syntax.properties.forEach((node) => this.visitProperty(node));
3932
+ }
3933
+ visitModule(syntax) {
3934
+ this.visitNode(syntax);
3935
+ syntax.fileImports.forEach((node) => this.visitFileImport(node));
3936
+ if (syntax.authorize !== null)
3937
+ this.visitAuthorize(syntax.authorize);
3938
+ syntax.features.forEach((node) => this.visitFeature(node));
3939
+ }
3940
+ visitFeature(syntax) {
3941
+ this.visitNode(syntax);
3942
+ syntax.fileImports.forEach((node) => this.visitFileImport(node));
3943
+ if (syntax.authorize !== null)
3944
+ this.visitAuthorize(syntax.authorize);
3945
+ syntax.features.forEach((node) => this.visitFeature(node));
3946
+ syntax.slices.forEach((node) => this.visitSlice(node));
3947
+ }
3948
+ visitSlice(syntax) {
3949
+ this.visitNode(syntax);
3950
+ syntax.commands.forEach((node) => this.visitCommand(node));
3951
+ syntax.events.forEach((node) => this.visitEvent(node));
3952
+ syntax.constraints.forEach((node) => this.visitConstraint(node));
3953
+ syntax.queries.forEach((node) => this.visitQuery(node));
3954
+ syntax.projections.forEach((node) => this.visitProjection(node));
3955
+ syntax.readModels.forEach((node) => this.visitReadModel(node));
3956
+ syntax.captures.forEach((node) => this.visitCapture(node));
3957
+ syntax.reactions.forEach((node) => this.visitReaction(node));
3958
+ syntax.screens.forEach((node) => this.visitScreen(node));
3959
+ syntax.specifications.forEach((node) => this.visitSpecification(node));
3960
+ }
3961
+ visitCommand(syntax) {
3962
+ this.visitNode(syntax);
3963
+ syntax.properties.forEach((node) => this.visitProperty(node));
3964
+ if (syntax.authorize !== null)
3965
+ this.visitAuthorize(syntax.authorize);
3966
+ syntax.validations.forEach((node) => this.visitValidate(node));
3967
+ syntax.produces.forEach((node) => this.visitProduces(node));
3968
+ }
3969
+ visitValidate(syntax) {
3970
+ this.visitNode(syntax);
3971
+ if (syntax.kind === "DeclarativeValidateSyntax")
3972
+ syntax.rules.forEach((node) => this.visitValidationRule(node));
3973
+ }
3974
+ visitValidationRule(syntax) {
3975
+ this.visitNode(syntax);
3976
+ if (syntax.value !== null)
3977
+ this.visitExpression(syntax.value);
3978
+ }
3979
+ visitEvent(syntax) {
3980
+ this.visitNode(syntax);
3981
+ syntax.properties.forEach((node) => this.visitProperty(node));
3982
+ syntax.tags.forEach((node) => this.visitTag(node));
3983
+ }
3984
+ visitTag(syntax) {
3985
+ this.visitNode(syntax);
3986
+ this.visitExpression(syntax.value);
3987
+ }
3988
+ visitReadModel(syntax) {
3989
+ this.visitNode(syntax);
3990
+ syntax.properties.forEach((node) => this.visitProperty(node));
3991
+ }
3992
+ visitConstraint(syntax) {
3993
+ this.visitNode(syntax);
3994
+ if (syntax.kind === "FileConstraintSyntax")
3995
+ this.visitNode(syntax.file);
3996
+ syntax.additionalRules.forEach((node) => this.visitConstraint(node));
3997
+ }
3998
+ visitQuery(syntax) {
3999
+ this.visitNode(syntax);
4000
+ this.visitTypeRef(syntax.returnType);
4001
+ if (syntax.by !== null)
4002
+ this.visitQueryParameter(syntax.by);
4003
+ syntax.filters.forEach((node) => this.visitQueryParameter(node));
4004
+ if (syntax.authorize !== null)
4005
+ this.visitAuthorize(syntax.authorize);
4006
+ }
4007
+ visitQueryParameter(syntax) {
4008
+ this.visitNode(syntax);
4009
+ this.visitTypeRef(syntax.type);
4010
+ }
4011
+ visitProjection(syntax) {
4012
+ this.visitNode(syntax);
4013
+ syntax.blocks.forEach((node) => this.visitProjectionBlock(node));
4014
+ }
4015
+ visitProjectionBlock(syntax) {
4016
+ this.visitNode(syntax);
4017
+ switch (syntax.kind) {
4018
+ case "FromSyntax":
4019
+ syntax.events.forEach((node) => this.visitNode(node));
4020
+ syntax.mappings.forEach((node) => this.visitMapping(node));
4021
+ break;
4022
+ case "EverySyntax":
4023
+ case "AllSyntax":
4024
+ syntax.mappings.forEach((node) => this.visitMapping(node));
4025
+ break;
4026
+ case "JoinSyntax":
4027
+ syntax.events.forEach((node) => this.visitJoinEvent(node));
4028
+ break;
4029
+ case "ProjectionVariantSyntax":
4030
+ syntax.entersOn.forEach((node) => this.visitNode(node));
4031
+ syntax.blocks.forEach((node) => this.visitProjectionBlock(node));
4032
+ break;
4033
+ case "ChildrenSyntax":
4034
+ case "NestedSyntax":
4035
+ syntax.blocks.forEach((node) => this.visitProjectionBlock(node));
4036
+ break;
4037
+ }
3895
4038
  }
3896
- if (isNode(value)) {
3897
- const result = { kind: value.kind };
3898
- const members = Object.keys(value).filter((member) => member !== "kind" && member !== "location").sort(ordinal);
3899
- for (const member of members) {
3900
- result[member] = write(value[member]);
4039
+ visitJoinEvent(syntax) {
4040
+ this.visitNode(syntax);
4041
+ syntax.mappings.forEach((node) => this.visitMapping(node));
4042
+ }
4043
+ visitMapping(syntax) {
4044
+ this.visitNode(syntax);
4045
+ }
4046
+ visitReaction(syntax) {
4047
+ this.visitNode(syntax);
4048
+ syntax.triggers.forEach((node) => this.visitReactionTrigger(node));
4049
+ }
4050
+ visitReactionTrigger(syntax) {
4051
+ this.visitNode(syntax);
4052
+ this.visitTriggerSource(syntax.source);
4053
+ syntax.produces.forEach((node) => this.visitProduces(node));
4054
+ syntax.invokes.forEach((node) => this.visitInvokes(node));
4055
+ }
4056
+ visitProduces(syntax) {
4057
+ this.visitNode(syntax);
4058
+ if (syntax.inlineEvent !== null)
4059
+ this.visitEvent(syntax.inlineEvent);
4060
+ if (syntax.for !== null)
4061
+ this.visitExpression(syntax.for);
4062
+ syntax.mappings.forEach((node) => this.visitPropertyMapping(node));
4063
+ syntax.tags.forEach((node) => this.visitTag(node));
4064
+ }
4065
+ visitInvokes(syntax) {
4066
+ this.visitNode(syntax);
4067
+ }
4068
+ visitCapture(syntax) {
4069
+ this.visitNode(syntax);
4070
+ if (syntax.source !== null)
4071
+ this.visitCaptureSource(syntax.source);
4072
+ syntax.appends.forEach((node) => this.visitCaptureAppend(node));
4073
+ syntax.children.forEach((node) => this.visitCaptureChildren(node));
4074
+ syntax.nested.forEach((node) => this.visitCaptureNested(node));
4075
+ }
4076
+ visitCaptureSource(syntax) {
4077
+ this.visitNode(syntax);
4078
+ syntax.settings.forEach((node) => this.visitCaptureSourceSetting(node));
4079
+ }
4080
+ visitCaptureSourceSetting(syntax) {
4081
+ this.visitNode(syntax);
4082
+ }
4083
+ visitCaptureAppend(syntax) {
4084
+ this.visitNode(syntax);
4085
+ }
4086
+ visitCaptureChildren(syntax) {
4087
+ this.visitNode(syntax);
4088
+ syntax.appends.forEach((node) => this.visitCaptureAppend(node));
4089
+ }
4090
+ visitCaptureNested(syntax) {
4091
+ this.visitNode(syntax);
4092
+ syntax.appends.forEach((node) => this.visitCaptureAppend(node));
4093
+ }
4094
+ visitTriggerSource(syntax) {
4095
+ this.visitNode(syntax);
4096
+ }
4097
+ visitScreen(syntax) {
4098
+ this.visitNode(syntax);
4099
+ syntax.directives.forEach((node) => this.visitScreenDirective(node));
4100
+ }
4101
+ visitScreenDirective(syntax) {
4102
+ this.visitNode(syntax);
4103
+ switch (syntax.kind) {
4104
+ case "ScreenDataSyntax":
4105
+ this.visitTypeRef(syntax.type);
4106
+ break;
4107
+ case "ScreenActionSyntax":
4108
+ if (syntax.navigate !== null)
4109
+ this.visitScreenDirective(syntax.navigate);
4110
+ break;
4111
+ case "ScreenTemplateReferenceSyntax":
4112
+ syntax.slots.forEach((slot2) => {
4113
+ this.visitNode(slot2);
4114
+ slot2.directives.forEach((node) => this.visitScreenDirective(node));
4115
+ });
4116
+ break;
4117
+ case "ScreenSectionSyntax":
4118
+ syntax.directives.forEach((node) => this.visitScreenDirective(node));
4119
+ break;
4120
+ case "ScreenTableSyntax":
4121
+ syntax.columns.forEach((node) => this.visitNode(node));
4122
+ if (syntax.rowClick !== null)
4123
+ this.visitScreenDirective(syntax.rowClick);
4124
+ break;
4125
+ case "ScreenSummarySyntax":
4126
+ syntax.fields.forEach((node) => this.visitNode(node));
4127
+ break;
4128
+ case "ScreenCodeSyntax":
4129
+ this.visitNode(syntax.code);
4130
+ break;
3901
4131
  }
3902
- return result;
3903
4132
  }
3904
- if (value === void 0) {
3905
- return null;
4133
+ visitSpecification(syntax) {
4134
+ this.visitNode(syntax);
4135
+ if (syntax.givenClock !== null)
4136
+ this.visitSpecificationClock(syntax.givenClock);
4137
+ syntax.given.forEach((node) => this.visitSpecificationEvent(node));
4138
+ syntax.givenReadModels.forEach((node) => this.visitSpecificationReadModel(node));
4139
+ if (syntax.when !== null)
4140
+ this.visitSpecificationCommand(syntax.when);
4141
+ syntax.givenCaptures.forEach((node) => this.visitSpecificationCapture(node));
4142
+ if (syntax.whenAppended !== null)
4143
+ this.visitSpecificationEvent(syntax.whenAppended);
4144
+ if (syntax.whenClock !== null)
4145
+ this.visitSpecificationClock(syntax.whenClock);
4146
+ if (syntax.whenTrigger !== null)
4147
+ this.visitSpecificationTrigger(syntax.whenTrigger);
4148
+ if (syntax.whenCapture !== null)
4149
+ this.visitSpecificationCapture(syntax.whenCapture);
4150
+ if (syntax.whenQuery !== null)
4151
+ this.visitSpecificationWhenQuery(syntax.whenQuery);
4152
+ syntax.thenEvents.forEach((node) => this.visitSpecificationEvent(node));
4153
+ syntax.thenReadModels.forEach((node) => this.visitSpecificationReadModel(node));
4154
+ syntax.thenResults.forEach((node) => this.visitSpecificationQueryResult(node));
4155
+ if (syntax.thenNoResult !== null)
4156
+ this.visitNode(syntax.thenNoResult);
4157
+ syntax.thenErrors.forEach((node) => this.visitNode(node));
4158
+ }
4159
+ visitSpecificationClock(syntax) {
4160
+ this.visitNode(syntax);
4161
+ }
4162
+ visitSpecificationTrigger(syntax) {
4163
+ this.visitNode(syntax);
4164
+ syntax.values.forEach((node) => this.visitPropertyMapping(node));
4165
+ }
4166
+ visitSpecificationCapture(syntax) {
4167
+ this.visitNode(syntax);
4168
+ syntax.record.forEach((node) => this.visitPropertyMapping(node));
4169
+ }
4170
+ visitSpecificationWhenQuery(syntax) {
4171
+ this.visitNode(syntax);
4172
+ syntax.arguments.forEach((node) => this.visitPropertyMapping(node));
4173
+ }
4174
+ visitSpecificationQueryResult(syntax) {
4175
+ this.visitNode(syntax);
4176
+ syntax.properties.forEach((node) => this.visitPropertyMapping(node));
4177
+ }
4178
+ visitSpecificationEvent(syntax) {
4179
+ this.visitNode(syntax);
4180
+ syntax.values.forEach((node) => this.visitPropertyMapping(node));
4181
+ if (syntax.for !== null)
4182
+ this.visitExpression(syntax.for);
4183
+ }
4184
+ visitSpecificationCommand(syntax) {
4185
+ this.visitNode(syntax);
4186
+ syntax.values.forEach((node) => this.visitPropertyMapping(node));
4187
+ if (syntax.for !== null)
4188
+ this.visitExpression(syntax.for);
4189
+ }
4190
+ visitSpecificationReadModel(syntax) {
4191
+ this.visitNode(syntax);
4192
+ syntax.properties.forEach((node) => this.visitPropertyMapping(node));
4193
+ }
4194
+ visitPropertyMapping(syntax) {
4195
+ this.visitNode(syntax);
4196
+ this.visitExpression(syntax.source);
4197
+ }
4198
+ visitProperty(syntax) {
4199
+ this.visitNode(syntax);
4200
+ this.visitTypeRef(syntax.type);
4201
+ }
4202
+ visitTypeRef(syntax) {
4203
+ this.visitNode(syntax);
4204
+ }
4205
+ visitExpression(syntax) {
4206
+ this.visitNode(syntax);
4207
+ if (syntax.kind === "ListExpressionSyntax") {
4208
+ syntax.items.forEach((item) => this.visitExpression(item));
4209
+ } else if (syntax.kind === "ObjectExpressionSyntax") {
4210
+ syntax.members.forEach((member) => this.visitObjectMember(member));
4211
+ }
4212
+ }
4213
+ visitObjectMember(syntax) {
4214
+ this.visitNode(syntax);
4215
+ this.visitExpression(syntax.value);
3906
4216
  }
3907
- return value;
3908
- }
3909
- function ordinal(left, right) {
3910
- return left < right ? -1 : left > right ? 1 : 0;
3911
- }
4217
+ };
3912
4218
 
3913
4219
  // ../../Compiler/dist/Authoring/QuickFixes.js
4220
+ function isQuickFixDiagnostic(code) {
4221
+ return code === DiagnosticCodes.LegacyOptionalSuffix || code === DiagnosticCodes.RedundantEventId;
4222
+ }
3914
4223
  function legacyOptionalTypeLength(line, diagnostic) {
3915
4224
  if (diagnostic.code !== DiagnosticCodes.LegacyOptionalSuffix)
3916
4225
  return 0;
@@ -3920,48 +4229,111 @@ function prepareQuickFixes(source, options = {}) {
3920
4229
  const original = parseForAuthoring(source, void 0, options.placement);
3921
4230
  if (!original.success)
3922
4231
  return () => [];
3923
- const lines = source.split(/\r\n|\r|\n/);
3924
- const starts = [0];
3925
- for (const match of source.matchAll(/\r\n|\r|\n/g))
3926
- starts.push(match.index + match[0].length);
3927
- const occurrences = original.diagnostics.filter((diagnostic) => diagnostic.code === DiagnosticCodes.LegacyOptionalSuffix).flatMap((diagnostic) => {
3928
- const length = legacyOptionalTypeLength(lines[diagnostic.location.line - 1], diagnostic);
3929
- return length === 0 ? [] : [{ diagnostic, edit: { start: starts[diagnostic.location.line - 1] + diagnostic.location.column + length - 2, length: 1, text: " optional" } }];
3930
- });
3931
- if (occurrences.length === 0)
3932
- return () => [];
4232
+ const lines = splitLines(source);
4233
+ const eventsByLine = /* @__PURE__ */ new Map();
4234
+ const walker = new class extends ScreenplaySyntaxWalker {
4235
+ visitEvent(event2) {
4236
+ eventsByLine.set(event2.location.line, event2);
4237
+ }
4238
+ }();
4239
+ walker.visitApplication(original.value);
4240
+ const counts = /* @__PURE__ */ new Map();
4241
+ const byLine = /* @__PURE__ */ new Map();
4242
+ const events = /* @__PURE__ */ new Map();
4243
+ let event;
4244
+ for (const line of lines) {
4245
+ event = eventsByLine.get(line.number) ?? event;
4246
+ if (event !== void 0)
4247
+ events.set(line.number, event);
4248
+ }
4249
+ for (const diagnostic of original.diagnostics) {
4250
+ counts.set(diagnostic.code, (counts.get(diagnostic.code) ?? 0) + 1);
4251
+ const line = lines[diagnostic.location.line - 1];
4252
+ const length = legacyOptionalTypeLength(line.raw, diagnostic);
4253
+ if (length > 0) {
4254
+ byLine.set(line.number, { line: line.number, fix: { diagnosticCode: diagnostic.code, title: "Use 'optional' instead of '?'", scope: "occurrence", edits: [{ start: line.startOffset + diagnostic.location.column + length - 2, length: 1, text: " optional" }] } });
4255
+ } else if (diagnostic.code === DiagnosticCodes.RedundantEventId && commentStart(line.raw) < 0) {
4256
+ const declaration = events.get(line.number);
4257
+ if (declaration?.id !== declaration?.name || declaration === void 0)
4258
+ continue;
4259
+ byLine.set(line.number, {
4260
+ line: line.number,
4261
+ fix: { diagnosticCode: diagnostic.code, title: "Remove the redundant event id", scope: "occurrence", edits: [{ start: line.startOffset, length: (lines[line.number]?.startOffset ?? source.length) - line.startOffset, text: "" }] },
4262
+ change: { node: declaration, replacement: { ...declaration, id: null } }
4263
+ });
4264
+ }
4265
+ }
4266
+ const optional = [...byLine.values()].filter((candidate) => candidate.fix.diagnosticCode === DiagnosticCodes.LegacyOptionalSuffix);
3933
4267
  const syntax = verificationShape(original);
3934
- const byLine = new Map(occurrences.map((occurrence) => [occurrence.diagnostic.location.line, occurrence]));
3935
- const verifiedOccurrences = /* @__PURE__ */ new Map();
4268
+ const verified = /* @__PURE__ */ new Map();
4269
+ let rangeVerdicts;
3936
4270
  let documentChecked = false;
3937
4271
  let documentFix;
3938
- const verify = (fix, line) => {
4272
+ const verify = (fix, change) => {
3939
4273
  const candidate = applyQuickFixEdits(source, fix.edits);
3940
4274
  if (candidate === void 0)
3941
4275
  return void 0;
3942
4276
  const parsed = parseForAuthoring(candidate, void 0, options.placement);
3943
- return parsed.success && verificationShape(parsed) === syntax && !parsed.diagnostics.some((diagnostic) => diagnostic.code === fix.diagnosticCode && (line === void 0 || line === diagnostic.location.line)) ? fix : void 0;
4277
+ if (!parsed.success || verificationShape(parsed) !== (change === void 0 ? syntax : verificationShape(original, [change])))
4278
+ return void 0;
4279
+ return parsed.diagnostics.filter((diagnostic) => diagnostic.code === fix.diagnosticCode).length === (counts.get(fix.diagnosticCode) ?? 0) - fix.edits.length ? fix : void 0;
3944
4280
  };
3945
- return (line) => {
3946
- if (!documentChecked) {
3947
- documentFix = verify({ diagnosticCode: DiagnosticCodes.LegacyOptionalSuffix, title: "Use 'optional' throughout this document", scope: "document", edits: occurrences.map((occurrence) => occurrence.edit) });
4281
+ return (line, diagnosticCode) => {
4282
+ const requests = typeof line === "number" || line === void 0 ? void 0 : line;
4283
+ const includeDocument = requests === void 0 ? diagnosticCode === void 0 || diagnosticCode === DiagnosticCodes.LegacyOptionalSuffix : requests.some((request) => request.diagnosticCode === DiagnosticCodes.LegacyOptionalSuffix);
4284
+ if (includeDocument && !documentChecked) {
4285
+ if (optional.length > 0)
4286
+ documentFix = verify({ diagnosticCode: DiagnosticCodes.LegacyOptionalSuffix, title: "Use 'optional' throughout this document", scope: "document", edits: optional.flatMap((candidate) => candidate.fix.edits) });
3948
4287
  documentChecked = true;
3949
4288
  }
3950
- const selected = line === void 0 ? void 0 : byLine.get(line);
4289
+ if (requests !== void 0) {
4290
+ if (rangeVerdicts === void 0) {
4291
+ rangeVerdicts = /* @__PURE__ */ new Map();
4292
+ const candidates = lines.flatMap((line2) => byLine.has(line2.number) ? [byLine.get(line2.number)] : []);
4293
+ const edits = candidates.flatMap((candidate) => candidate.fix.edits);
4294
+ const candidateSource = applyQuickFixEdits(source, edits);
4295
+ if (edits.length > 0 && candidateSource !== void 0) {
4296
+ const parsed = parseForAuthoring(candidateSource, void 0, options.placement);
4297
+ const changes = candidates.flatMap((candidate) => candidate.change === void 0 ? [] : [candidate.change]);
4298
+ if (parsed.success && verificationShape(parsed) === verificationShape(original, changes)) {
4299
+ const remaining = /* @__PURE__ */ new Map();
4300
+ const removed = /* @__PURE__ */ new Map();
4301
+ for (const diagnostic of parsed.diagnostics)
4302
+ remaining.set(diagnostic.code, (remaining.get(diagnostic.code) ?? 0) + 1);
4303
+ for (const candidate of candidates)
4304
+ removed.set(candidate.fix.diagnosticCode, (removed.get(candidate.fix.diagnosticCode) ?? 0) + candidate.fix.edits.length);
4305
+ if ([...removed].every(([code, count]) => (remaining.get(code) ?? 0) === (counts.get(code) ?? 0) - count)) {
4306
+ for (const candidate of candidates)
4307
+ rangeVerdicts.set(candidate.line, candidate.fix);
4308
+ }
4309
+ }
4310
+ }
4311
+ }
4312
+ const selected2 = /* @__PURE__ */ new Map();
4313
+ for (const request of requests) {
4314
+ const fix = rangeVerdicts.get(request.line);
4315
+ if (fix?.diagnosticCode === request.diagnosticCode)
4316
+ selected2.set(request.line, fix);
4317
+ }
4318
+ return [...selected2.values(), ...includeDocument && documentFix !== void 0 ? [documentFix] : []];
4319
+ }
4320
+ const occurrenceLine = typeof line === "number" ? line : void 0;
4321
+ const selected = occurrenceLine === void 0 ? void 0 : byLine.get(occurrenceLine);
3951
4322
  let occurrenceFix;
3952
- if (selected !== void 0 && line !== void 0) {
3953
- if (!verifiedOccurrences.has(line)) {
3954
- if (verifiedOccurrences.size === 32)
3955
- verifiedOccurrences.delete(verifiedOccurrences.keys().next().value);
3956
- verifiedOccurrences.set(line, verify({ diagnosticCode: DiagnosticCodes.LegacyOptionalSuffix, title: "Use 'optional' instead of '?'", scope: "occurrence", edits: [selected.edit] }, line));
4323
+ if (selected !== void 0 && occurrenceLine !== void 0 && (diagnosticCode === void 0 || selected.fix.diagnosticCode === diagnosticCode)) {
4324
+ if (!verified.has(occurrenceLine)) {
4325
+ if (verified.size === 32)
4326
+ verified.delete(verified.keys().next().value);
4327
+ verified.set(occurrenceLine, verify(selected.fix, selected.change));
3957
4328
  }
3958
- occurrenceFix = verifiedOccurrences.get(line);
4329
+ occurrenceFix = verified.get(occurrenceLine);
3959
4330
  }
3960
- return [occurrenceFix, documentFix].filter((fix) => fix !== void 0);
4331
+ return [occurrenceFix, includeDocument ? documentFix : void 0].filter((fix) => fix !== void 0);
3961
4332
  };
3962
4333
  }
3963
- function verificationShape(parsed) {
3964
- return JSON.stringify([toSyntaxJson(parsed.value), parsed.triggerData.map((value) => toSyntaxJson(value))]);
4334
+ function verificationShape(parsed, changes = []) {
4335
+ const replacements = new Map(changes.map((change) => [change.node, change.replacement]));
4336
+ return JSON.stringify([parsed.value, parsed.triggerData], (key, value) => key === "location" ? void 0 : replacements.get(value) ?? value);
3965
4337
  }
3966
4338
  function applyQuickFixEdits(source, edits) {
3967
4339
  const parts = [];
@@ -4451,6 +4823,7 @@ export {
4451
4823
  productionDestinations,
4452
4824
  destinationHints,
4453
4825
  DiagnosticCodes,
4826
+ isQuickFixDiagnostic,
4454
4827
  prepareQuickFixes,
4455
4828
  diagnosticCodes,
4456
4829
  isFileImportLine,
@@ -4460,4 +4833,4 @@ export {
4460
4833
  validateLines,
4461
4834
  validateEventContextPaths
4462
4835
  };
4463
- //# sourceMappingURL=chunk-7WJEZ72S.js.map
4836
+ //# sourceMappingURL=chunk-GJX4NJYC.js.map