@cratis/screenplay-language 0.1.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.
Files changed (147) hide show
  1. package/README.md +52 -0
  2. package/completion-items.ts +128 -0
  3. package/completion-planner.ts +111 -0
  4. package/completions.ts +112 -0
  5. package/diagnostics.ts +52 -0
  6. package/dist/completion-items.d.ts +23 -0
  7. package/dist/completion-items.d.ts.map +1 -0
  8. package/dist/completion-items.js +105 -0
  9. package/dist/completion-items.js.map +1 -0
  10. package/dist/completion-planner.d.ts +25 -0
  11. package/dist/completion-planner.d.ts.map +1 -0
  12. package/dist/completion-planner.js +90 -0
  13. package/dist/completion-planner.js.map +1 -0
  14. package/dist/completions.d.ts +4 -0
  15. package/dist/completions.d.ts.map +1 -0
  16. package/dist/completions.js +78 -0
  17. package/dist/completions.js.map +1 -0
  18. package/dist/diagnostics.d.ts +5 -0
  19. package/dist/diagnostics.d.ts.map +1 -0
  20. package/dist/diagnostics.js +47 -0
  21. package/dist/diagnostics.js.map +1 -0
  22. package/dist/document-context.d.ts +5 -0
  23. package/dist/document-context.d.ts.map +1 -0
  24. package/dist/document-context.js +45 -0
  25. package/dist/document-context.js.map +1 -0
  26. package/dist/hover-content.d.ts +2 -0
  27. package/dist/hover-content.d.ts.map +1 -0
  28. package/dist/hover-content.js +64 -0
  29. package/dist/hover-content.js.map +1 -0
  30. package/dist/hover.d.ts +3 -0
  31. package/dist/hover.d.ts.map +1 -0
  32. package/dist/hover.js +15 -0
  33. package/dist/hover.js.map +1 -0
  34. package/dist/index.d.ts +23 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +54 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/keyword-docs.d.ts +4 -0
  39. package/dist/keyword-docs.d.ts.map +1 -0
  40. package/dist/keyword-docs.js +100 -0
  41. package/dist/keyword-docs.js.map +1 -0
  42. package/dist/language.d.ts +12 -0
  43. package/dist/language.d.ts.map +1 -0
  44. package/dist/language.js +117 -0
  45. package/dist/language.js.map +1 -0
  46. package/dist/sub-language-registry.d.ts +20 -0
  47. package/dist/sub-language-registry.d.ts.map +1 -0
  48. package/dist/sub-language-registry.js +18 -0
  49. package/dist/sub-language-registry.js.map +1 -0
  50. package/dist/sub-languages/capture/CompletionProvider.d.ts +32 -0
  51. package/dist/sub-languages/capture/CompletionProvider.d.ts.map +1 -0
  52. package/dist/sub-languages/capture/CompletionProvider.js +332 -0
  53. package/dist/sub-languages/capture/CompletionProvider.js.map +1 -0
  54. package/dist/sub-languages/capture/HoverProvider.d.ts +8 -0
  55. package/dist/sub-languages/capture/HoverProvider.d.ts.map +1 -0
  56. package/dist/sub-languages/capture/HoverProvider.js +87 -0
  57. package/dist/sub-languages/capture/HoverProvider.js.map +1 -0
  58. package/dist/sub-languages/capture/Validator.d.ts +6 -0
  59. package/dist/sub-languages/capture/Validator.d.ts.map +1 -0
  60. package/dist/sub-languages/capture/Validator.js +72 -0
  61. package/dist/sub-languages/capture/Validator.js.map +1 -0
  62. package/dist/sub-languages/capture/index.d.ts +6 -0
  63. package/dist/sub-languages/capture/index.d.ts.map +1 -0
  64. package/dist/sub-languages/capture/index.js +58 -0
  65. package/dist/sub-languages/capture/index.js.map +1 -0
  66. package/dist/sub-languages/capture/language.d.ts +6 -0
  67. package/dist/sub-languages/capture/language.d.ts.map +1 -0
  68. package/dist/sub-languages/capture/language.js +160 -0
  69. package/dist/sub-languages/capture/language.js.map +1 -0
  70. package/dist/sub-languages/cdl.d.ts +3 -0
  71. package/dist/sub-languages/cdl.d.ts.map +1 -0
  72. package/dist/sub-languages/cdl.js +74 -0
  73. package/dist/sub-languages/cdl.js.map +1 -0
  74. package/dist/sub-languages/pdl.d.ts +3 -0
  75. package/dist/sub-languages/pdl.d.ts.map +1 -0
  76. package/dist/sub-languages/pdl.js +67 -0
  77. package/dist/sub-languages/pdl.js.map +1 -0
  78. package/dist/sub-languages/projection/CodeActionProvider.d.ts +18 -0
  79. package/dist/sub-languages/projection/CodeActionProvider.d.ts.map +1 -0
  80. package/dist/sub-languages/projection/CodeActionProvider.js +93 -0
  81. package/dist/sub-languages/projection/CodeActionProvider.js.map +1 -0
  82. package/dist/sub-languages/projection/CompletionProvider.d.ts +41 -0
  83. package/dist/sub-languages/projection/CompletionProvider.d.ts.map +1 -0
  84. package/dist/sub-languages/projection/CompletionProvider.js +591 -0
  85. package/dist/sub-languages/projection/CompletionProvider.js.map +1 -0
  86. package/dist/sub-languages/projection/HoverProvider.d.ts +24 -0
  87. package/dist/sub-languages/projection/HoverProvider.d.ts.map +1 -0
  88. package/dist/sub-languages/projection/HoverProvider.js +298 -0
  89. package/dist/sub-languages/projection/HoverProvider.js.map +1 -0
  90. package/dist/sub-languages/projection/Validator.d.ts +22 -0
  91. package/dist/sub-languages/projection/Validator.d.ts.map +1 -0
  92. package/dist/sub-languages/projection/Validator.js +339 -0
  93. package/dist/sub-languages/projection/Validator.js.map +1 -0
  94. package/dist/sub-languages/projection/index.d.ts +49 -0
  95. package/dist/sub-languages/projection/index.d.ts.map +1 -0
  96. package/dist/sub-languages/projection/index.js +160 -0
  97. package/dist/sub-languages/projection/index.js.map +1 -0
  98. package/dist/sub-languages/projection/language.d.ts +5 -0
  99. package/dist/sub-languages/projection/language.d.ts.map +1 -0
  100. package/dist/sub-languages/projection/language.js +171 -0
  101. package/dist/sub-languages/projection/language.js.map +1 -0
  102. package/dist/symbols.d.ts +45 -0
  103. package/dist/symbols.d.ts.map +1 -0
  104. package/dist/symbols.js +100 -0
  105. package/dist/symbols.js.map +1 -0
  106. package/dist/themes/screenplay-dark.d.ts +4 -0
  107. package/dist/themes/screenplay-dark.d.ts.map +1 -0
  108. package/dist/themes/screenplay-dark.js +29 -0
  109. package/dist/themes/screenplay-dark.js.map +1 -0
  110. package/dist/themes/screenplay-light.d.ts +4 -0
  111. package/dist/themes/screenplay-light.d.ts.map +1 -0
  112. package/dist/themes/screenplay-light.js +29 -0
  113. package/dist/themes/screenplay-light.js.map +1 -0
  114. package/dist/tokens.d.ts +5 -0
  115. package/dist/tokens.d.ts.map +1 -0
  116. package/dist/tokens.js +121 -0
  117. package/dist/tokens.js.map +1 -0
  118. package/dist/validation.d.ts +10 -0
  119. package/dist/validation.d.ts.map +1 -0
  120. package/dist/validation.js +101 -0
  121. package/dist/validation.js.map +1 -0
  122. package/document-context.ts +51 -0
  123. package/hover-content.ts +76 -0
  124. package/hover.ts +23 -0
  125. package/index.ts +98 -0
  126. package/keyword-docs.ts +102 -0
  127. package/language.ts +130 -0
  128. package/package.json +44 -0
  129. package/sub-language-registry.ts +42 -0
  130. package/sub-languages/capture/CompletionProvider.ts +365 -0
  131. package/sub-languages/capture/HoverProvider.ts +99 -0
  132. package/sub-languages/capture/Validator.ts +97 -0
  133. package/sub-languages/capture/index.ts +72 -0
  134. package/sub-languages/capture/language.ts +176 -0
  135. package/sub-languages/cdl.ts +76 -0
  136. package/sub-languages/pdl.ts +69 -0
  137. package/sub-languages/projection/CodeActionProvider.ts +123 -0
  138. package/sub-languages/projection/CompletionProvider.ts +653 -0
  139. package/sub-languages/projection/HoverProvider.ts +338 -0
  140. package/sub-languages/projection/Validator.ts +387 -0
  141. package/sub-languages/projection/index.ts +223 -0
  142. package/sub-languages/projection/language.ts +193 -0
  143. package/symbols.ts +161 -0
  144. package/themes/screenplay-dark.ts +32 -0
  145. package/themes/screenplay-light.ts +32 -0
  146. package/tokens.ts +145 -0
  147. package/validation.ts +145 -0
package/validation.ts ADDED
@@ -0,0 +1,145 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { primitiveTypes, sliceTypes } from './language';
5
+ import { fenceMap, indentOf } from './document-context';
6
+ import { knownEventNames, scanDocument } from './symbols';
7
+
8
+ export type ValidationSeverity = 'error' | 'warning';
9
+
10
+ export interface ValidationIssue {
11
+ line: number;
12
+ startColumn: number;
13
+ endColumn: number;
14
+ message: string;
15
+ severity: ValidationSeverity;
16
+ }
17
+
18
+ function issue(
19
+ severity: ValidationSeverity,
20
+ line: number,
21
+ startColumn: number,
22
+ length: number,
23
+ message: string,
24
+ ): ValidationIssue {
25
+ return { severity, message, line, startColumn, endColumn: startColumn + length };
26
+ }
27
+
28
+ function tokenIssue(
29
+ severity: ValidationSeverity,
30
+ line: number,
31
+ text: string,
32
+ token: string,
33
+ message: string,
34
+ ): ValidationIssue {
35
+ const column = text.indexOf(token) + 1;
36
+ return issue(severity, line, column, token.length, message);
37
+ }
38
+
39
+ // Validates a Screenplay document without any editor dependency — both the Monaco
40
+ // service and the VSCode extension adapt these issues to their marker/diagnostic APIs.
41
+ export function validateLines(lines: string[]): ValidationIssue[] {
42
+ const fences = fenceMap(lines);
43
+ const symbols = scanDocument(lines);
44
+ const events = new Set(knownEventNames(symbols));
45
+ const policies = new Set(symbols.policies.map((policy) => policy.name));
46
+ const issues: ValidationIssue[] = [];
47
+
48
+ const checkEvent = (line: number, text: string, name: string) => {
49
+ if (!events.has(name)) {
50
+ issues.push(
51
+ tokenIssue('warning', line, text, name, `Unknown event type '${name}' — declare it in a slice or import it.`),
52
+ );
53
+ }
54
+ };
55
+
56
+ let authorizeIndent = -1;
57
+
58
+ for (let index = 0; index < lines.length; index++) {
59
+ const line = lines[index];
60
+ const trimmed = line.trim();
61
+
62
+ if (fences[index] || trimmed.length === 0) continue;
63
+
64
+ const leading = line.match(/^[ ]*(\t+)/);
65
+ if (leading) {
66
+ issues.push(
67
+ issue('warning', index, leading[0].length - leading[1].length + 1, leading[1].length, 'Screenplay is indentation-based — use spaces, not tabs.'),
68
+ );
69
+ }
70
+
71
+ // Policy references continue on indented lines below an authorize clause.
72
+ if (authorizeIndent >= 0) {
73
+ if (
74
+ indentOf(line) > authorizeIndent &&
75
+ /^(?:or\s+)?[A-Z]\w*(?:\s+or\s+[A-Z]\w*)*$/.test(trimmed)
76
+ ) {
77
+ for (const name of trimmed.split(/\s+/).filter((token) => token !== 'or')) {
78
+ if (!policies.has(name)) {
79
+ issues.push(tokenIssue('warning', index, line, name, `Unknown policy '${name}' — declare it with 'policy ${name}'.`));
80
+ }
81
+ }
82
+ continue;
83
+ }
84
+ authorizeIndent = -1;
85
+ }
86
+
87
+ const slice = trimmed.match(/^slice\s+(\w+)/);
88
+ if (slice && !sliceTypes.includes(slice[1])) {
89
+ issues.push(
90
+ tokenIssue('error', index, line, slice[1], `Unknown slice type '${slice[1]}' — expected ${sliceTypes.join(', ')}.`),
91
+ );
92
+ }
93
+
94
+ const concept = trimmed.match(/^concept\s+\w+\s*:\s*(\w+)/);
95
+ if (concept && concept[1] !== 'Enum' && !primitiveTypes.includes(concept[1])) {
96
+ issues.push(
97
+ tokenIssue('error', index, line, concept[1], `Unknown primitive type '${concept[1]}' — expected ${primitiveTypes.join(', ')} or Enum.`),
98
+ );
99
+ }
100
+
101
+ const authorize = trimmed.match(/^authorize\s+(.*)$/);
102
+ if (authorize) {
103
+ authorizeIndent = indentOf(line);
104
+ for (const name of authorize[1].split(/\s+/).filter((token) => token !== 'or' && token.length > 0)) {
105
+ if (!policies.has(name)) {
106
+ issues.push(tokenIssue('warning', index, line, name, `Unknown policy '${name}' — declare it with 'policy ${name}'.`));
107
+ }
108
+ }
109
+ }
110
+
111
+ const reactsOn = trimmed.match(/^on\s+([A-Z]\w*)\s*$/);
112
+ if (reactsOn) checkEvent(index, line, reactsOn[1]);
113
+
114
+ const produces = trimmed.match(/^produces\s+([A-Z]\w*)\s*$/);
115
+ if (produces) checkEvent(index, line, produces[1]);
116
+
117
+ if (/^produces\s+when\b/.test(trimmed)) {
118
+ for (let next = index + 1; next < lines.length; next++) {
119
+ const candidate = lines[next];
120
+ if (fences[next] || candidate.trim().length === 0) continue;
121
+ if (indentOf(candidate) > indentOf(line)) {
122
+ const name = candidate.trim().match(/^([A-Z]\w*)\s*$/);
123
+ if (name) checkEvent(next, candidate, name[1]);
124
+ }
125
+ break;
126
+ }
127
+ }
128
+
129
+ const uniqueEvent = trimmed.match(/^unique\s+event\s+([A-Z]\w*)/);
130
+ if (uniqueEvent) checkEvent(index, line, uniqueEvent[1]);
131
+
132
+ const uniqueProperty = trimmed.match(/^unique\s+[a-z_]\w*\s+on\s+([A-Z]\w*)/);
133
+ if (uniqueProperty) checkEvent(index, line, uniqueProperty[1]);
134
+ }
135
+
136
+ const fenceLines = lines
137
+ .map((line, index) => ({ line, index }))
138
+ .filter(({ line }) => /^\s*```\s*$/.test(line));
139
+ if (fenceLines.length % 2 === 1) {
140
+ const last = fenceLines[fenceLines.length - 1];
141
+ issues.push(issue('error', last.index, 1, last.line.length + 1, 'Unclosed inline code block — expected a closing ``` line.'));
142
+ }
143
+
144
+ return issues;
145
+ }