@cratis/screenplay-language 4.1.0 → 4.2.1
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 +1 -1
- package/diagnostic-codes.ts +27 -0
- package/diagnostics.ts +1 -0
- package/dist/diagnostic-codes.d.ts +16 -0
- package/dist/diagnostic-codes.d.ts.map +1 -0
- package/dist/diagnostic-codes.js +25 -0
- package/dist/diagnostic-codes.js.map +1 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +1 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/file-references.d.ts +11 -0
- package/dist/file-references.d.ts.map +1 -0
- package/dist/file-references.js +70 -0
- package/dist/file-references.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +5 -1
- package/dist/symbols.js.map +1 -1
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +18 -17
- package/dist/validation.js.map +1 -1
- package/file-references.ts +87 -0
- package/for_fileReferences/when_recognizing_an_absolute_path/with_paths_of_every_shape.ts +26 -0
- package/for_fileReferences/when_scanning_a_document/inside_a_code_fence.ts +26 -0
- package/for_fileReferences/when_scanning_a_document/with_a_bare_file_keyword.ts +19 -0
- package/for_fileReferences/when_scanning_a_document/with_a_file_directive.ts +30 -0
- package/for_fileReferences/when_scanning_a_document/with_a_property_named_file.ts +25 -0
- package/for_fileReferences/when_scanning_a_document/with_a_trailing_comment.ts +25 -0
- package/for_fileReferences/when_scanning_a_document/with_directives_on_several_constructs.ts +41 -0
- package/for_scanDocument/when_scanning_a_query/with_a_dotted_return_type.ts +34 -0
- package/for_validateLines/when_validating_a_document/with_a_problem_of_every_coded_kind.ts +52 -0
- package/for_validateLines/when_validating_a_document/with_an_unknown_policy.ts +33 -0
- package/index.ts +9 -0
- package/package.json +8 -4
- package/symbols.ts +5 -1
- package/validation.ts +98 -12
- package/vitest.setup.ts +5 -0
package/validation.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
4
|
import { causedByProperties, contextRoots, identityProperties, primitiveTypes, sliceTypes } from './language';
|
|
5
|
+
import { DiagnosticCode, diagnosticCodes } from './diagnostic-codes';
|
|
5
6
|
import { fenceMap, indentOf } from './document-context';
|
|
6
7
|
import {
|
|
7
8
|
DocumentSymbols,
|
|
@@ -19,6 +20,10 @@ export interface ValidationIssue {
|
|
|
19
20
|
endColumn: number;
|
|
20
21
|
message: string;
|
|
21
22
|
severity: ValidationSeverity;
|
|
23
|
+
|
|
24
|
+
// The compiler code for this condition, absent on the structural checks the editor makes and the
|
|
25
|
+
// compiler does not. See ./diagnostic-codes.ts.
|
|
26
|
+
code?: DiagnosticCode;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
function issue(
|
|
@@ -27,8 +32,9 @@ function issue(
|
|
|
27
32
|
startColumn: number,
|
|
28
33
|
length: number,
|
|
29
34
|
message: string,
|
|
35
|
+
code: DiagnosticCode,
|
|
30
36
|
): ValidationIssue {
|
|
31
|
-
return { severity, message, line, startColumn, endColumn: startColumn + length };
|
|
37
|
+
return { severity, message, line, startColumn, endColumn: startColumn + length, code };
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
function tokenIssue(
|
|
@@ -37,9 +43,10 @@ function tokenIssue(
|
|
|
37
43
|
text: string,
|
|
38
44
|
token: string,
|
|
39
45
|
message: string,
|
|
46
|
+
code: DiagnosticCode,
|
|
40
47
|
): ValidationIssue {
|
|
41
48
|
const column = text.indexOf(token) + 1;
|
|
42
|
-
return issue(severity, line, column, token.length, message);
|
|
49
|
+
return issue(severity, line, column, token.length, message, code);
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
// Reports every property whose type reference names nothing the document declares, and
|
|
@@ -60,6 +67,7 @@ function validateDeclarations(lines: string[], symbols: DocumentSymbols): Valida
|
|
|
60
67
|
lines[property.line],
|
|
61
68
|
property.type,
|
|
62
69
|
`Unknown type '${bare}' on '${property.name}' of ${owner} — declare it with 'concept ${bare} : <Primitive>' or 'type ${bare}'.`,
|
|
70
|
+
diagnosticCodes.unknownType,
|
|
63
71
|
),
|
|
64
72
|
);
|
|
65
73
|
}
|
|
@@ -79,6 +87,7 @@ function validateDeclarations(lines: string[], symbols: DocumentSymbols): Valida
|
|
|
79
87
|
lines[extra.line],
|
|
80
88
|
'identifier',
|
|
81
89
|
`Command '${command.name}' already marks '${identifiers[0].name}' as identifier — only one property can be the identifier.`,
|
|
90
|
+
diagnosticCodes.duplicateCommandIdentifier,
|
|
82
91
|
),
|
|
83
92
|
);
|
|
84
93
|
}
|
|
@@ -98,6 +107,7 @@ function validateDeclarations(lines: string[], symbols: DocumentSymbols): Valida
|
|
|
98
107
|
lines[declaration.line],
|
|
99
108
|
declaration.name,
|
|
100
109
|
`Duplicate declaration of '${declaration.name}' — concept and type names must be unique.`,
|
|
110
|
+
diagnosticCodes.duplicateDeclaration,
|
|
101
111
|
),
|
|
102
112
|
);
|
|
103
113
|
}
|
|
@@ -117,7 +127,14 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
117
127
|
const checkEvent = (line: number, text: string, name: string) => {
|
|
118
128
|
if (!events.has(name)) {
|
|
119
129
|
issues.push(
|
|
120
|
-
tokenIssue(
|
|
130
|
+
tokenIssue(
|
|
131
|
+
'warning',
|
|
132
|
+
line,
|
|
133
|
+
text,
|
|
134
|
+
name,
|
|
135
|
+
`Unknown event type '${name}' — declare it in a slice or import it.`,
|
|
136
|
+
diagnosticCodes.unknownEvent,
|
|
137
|
+
),
|
|
121
138
|
);
|
|
122
139
|
}
|
|
123
140
|
};
|
|
@@ -133,7 +150,14 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
133
150
|
const leading = line.match(/^[ ]*(\t+)/);
|
|
134
151
|
if (leading) {
|
|
135
152
|
issues.push(
|
|
136
|
-
issue(
|
|
153
|
+
issue(
|
|
154
|
+
'warning',
|
|
155
|
+
index,
|
|
156
|
+
leading[0].length - leading[1].length + 1,
|
|
157
|
+
leading[1].length,
|
|
158
|
+
'Screenplay is indentation-based — use spaces, not tabs.',
|
|
159
|
+
diagnosticCodes.tabIndentation,
|
|
160
|
+
),
|
|
137
161
|
);
|
|
138
162
|
}
|
|
139
163
|
|
|
@@ -145,7 +169,16 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
145
169
|
) {
|
|
146
170
|
for (const name of trimmed.split(/\s+/).filter((token) => token !== 'or')) {
|
|
147
171
|
if (!policies.has(name)) {
|
|
148
|
-
issues.push(
|
|
172
|
+
issues.push(
|
|
173
|
+
tokenIssue(
|
|
174
|
+
'warning',
|
|
175
|
+
index,
|
|
176
|
+
line,
|
|
177
|
+
name,
|
|
178
|
+
`Unknown policy '${name}' — declare it with 'policy ${name}'.`,
|
|
179
|
+
diagnosticCodes.unknownPolicy,
|
|
180
|
+
),
|
|
181
|
+
);
|
|
149
182
|
}
|
|
150
183
|
}
|
|
151
184
|
continue;
|
|
@@ -156,14 +189,28 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
156
189
|
const slice = trimmed.match(/^slice\s+(\w+)/);
|
|
157
190
|
if (slice && !sliceTypes.includes(slice[1])) {
|
|
158
191
|
issues.push(
|
|
159
|
-
tokenIssue(
|
|
192
|
+
tokenIssue(
|
|
193
|
+
'error',
|
|
194
|
+
index,
|
|
195
|
+
line,
|
|
196
|
+
slice[1],
|
|
197
|
+
`Unknown slice type '${slice[1]}' — expected ${sliceTypes.join(', ')}.`,
|
|
198
|
+
diagnosticCodes.unknownSliceType,
|
|
199
|
+
),
|
|
160
200
|
);
|
|
161
201
|
}
|
|
162
202
|
|
|
163
203
|
const concept = trimmed.match(/^concept\s+\w+\s*:\s*(\w+)/);
|
|
164
204
|
if (concept && concept[1] !== 'Enum' && !primitiveTypes.includes(concept[1])) {
|
|
165
205
|
issues.push(
|
|
166
|
-
tokenIssue(
|
|
206
|
+
tokenIssue(
|
|
207
|
+
'error',
|
|
208
|
+
index,
|
|
209
|
+
line,
|
|
210
|
+
concept[1],
|
|
211
|
+
`Unknown primitive type '${concept[1]}' — expected ${primitiveTypes.join(', ')} or Enum.`,
|
|
212
|
+
diagnosticCodes.unknownPrimitiveType,
|
|
213
|
+
),
|
|
167
214
|
);
|
|
168
215
|
}
|
|
169
216
|
|
|
@@ -172,7 +219,16 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
172
219
|
authorizeIndent = indentOf(line);
|
|
173
220
|
for (const name of authorize[1].split(/\s+/).filter((token) => token !== 'or' && token.length > 0)) {
|
|
174
221
|
if (!policies.has(name)) {
|
|
175
|
-
issues.push(
|
|
222
|
+
issues.push(
|
|
223
|
+
tokenIssue(
|
|
224
|
+
'warning',
|
|
225
|
+
index,
|
|
226
|
+
line,
|
|
227
|
+
name,
|
|
228
|
+
`Unknown policy '${name}' — declare it with 'policy ${name}'.`,
|
|
229
|
+
diagnosticCodes.unknownPolicy,
|
|
230
|
+
),
|
|
231
|
+
);
|
|
176
232
|
}
|
|
177
233
|
}
|
|
178
234
|
}
|
|
@@ -206,7 +262,14 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
206
262
|
const segments = path.substring('$context.'.length).split('.');
|
|
207
263
|
if (!contextRoots.includes(segments[0])) {
|
|
208
264
|
issues.push(
|
|
209
|
-
tokenIssue(
|
|
265
|
+
tokenIssue(
|
|
266
|
+
'warning',
|
|
267
|
+
index,
|
|
268
|
+
line,
|
|
269
|
+
path,
|
|
270
|
+
`Unknown $context path '${segments.join('.')}' — expected one of ${contextRoots.join(', ')}.`,
|
|
271
|
+
diagnosticCodes.unknownContextPath,
|
|
272
|
+
),
|
|
210
273
|
);
|
|
211
274
|
} else if (
|
|
212
275
|
segments[0] === 'causedBy' &&
|
|
@@ -214,7 +277,14 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
214
277
|
!causedByProperties.includes(segments[1])
|
|
215
278
|
) {
|
|
216
279
|
issues.push(
|
|
217
|
-
tokenIssue(
|
|
280
|
+
tokenIssue(
|
|
281
|
+
'warning',
|
|
282
|
+
index,
|
|
283
|
+
line,
|
|
284
|
+
path,
|
|
285
|
+
`Unknown $context.causedBy property '${segments[1]}' — expected ${causedByProperties.join(', ')}.`,
|
|
286
|
+
diagnosticCodes.unknownContextCausedByProperty,
|
|
287
|
+
),
|
|
218
288
|
);
|
|
219
289
|
} else if (
|
|
220
290
|
segments[0] === 'identity' &&
|
|
@@ -222,7 +292,14 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
222
292
|
!identityProperties.includes(segments[1])
|
|
223
293
|
) {
|
|
224
294
|
issues.push(
|
|
225
|
-
tokenIssue(
|
|
295
|
+
tokenIssue(
|
|
296
|
+
'warning',
|
|
297
|
+
index,
|
|
298
|
+
line,
|
|
299
|
+
path,
|
|
300
|
+
`Unknown $context.identity property '${segments[1]}' — expected ${identityProperties.join(', ')}.`,
|
|
301
|
+
diagnosticCodes.unknownContextIdentityProperty,
|
|
302
|
+
),
|
|
226
303
|
);
|
|
227
304
|
}
|
|
228
305
|
}
|
|
@@ -233,7 +310,16 @@ export function validateLines(lines: string[]): ValidationIssue[] {
|
|
|
233
310
|
.filter(({ line }) => /^\s*```\s*$/.test(line));
|
|
234
311
|
if (fenceLines.length % 2 === 1) {
|
|
235
312
|
const last = fenceLines[fenceLines.length - 1];
|
|
236
|
-
issues.push(
|
|
313
|
+
issues.push(
|
|
314
|
+
issue(
|
|
315
|
+
'error',
|
|
316
|
+
last.index,
|
|
317
|
+
1,
|
|
318
|
+
last.line.length + 1,
|
|
319
|
+
'Unclosed inline code block — expected a closing ``` line.',
|
|
320
|
+
diagnosticCodes.unclosedCodeBlock,
|
|
321
|
+
),
|
|
322
|
+
);
|
|
237
323
|
}
|
|
238
324
|
|
|
239
325
|
return issues;
|
package/vitest.setup.ts
ADDED