@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.
- package/README.md +52 -0
- package/completion-items.ts +128 -0
- package/completion-planner.ts +111 -0
- package/completions.ts +112 -0
- package/diagnostics.ts +52 -0
- package/dist/completion-items.d.ts +23 -0
- package/dist/completion-items.d.ts.map +1 -0
- package/dist/completion-items.js +105 -0
- package/dist/completion-items.js.map +1 -0
- package/dist/completion-planner.d.ts +25 -0
- package/dist/completion-planner.d.ts.map +1 -0
- package/dist/completion-planner.js +90 -0
- package/dist/completion-planner.js.map +1 -0
- package/dist/completions.d.ts +4 -0
- package/dist/completions.d.ts.map +1 -0
- package/dist/completions.js +78 -0
- package/dist/completions.js.map +1 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +47 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/document-context.d.ts +5 -0
- package/dist/document-context.d.ts.map +1 -0
- package/dist/document-context.js +45 -0
- package/dist/document-context.js.map +1 -0
- package/dist/hover-content.d.ts +2 -0
- package/dist/hover-content.d.ts.map +1 -0
- package/dist/hover-content.js +64 -0
- package/dist/hover-content.js.map +1 -0
- package/dist/hover.d.ts +3 -0
- package/dist/hover.d.ts.map +1 -0
- package/dist/hover.js +15 -0
- package/dist/hover.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/keyword-docs.d.ts +4 -0
- package/dist/keyword-docs.d.ts.map +1 -0
- package/dist/keyword-docs.js +100 -0
- package/dist/keyword-docs.js.map +1 -0
- package/dist/language.d.ts +12 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +117 -0
- package/dist/language.js.map +1 -0
- package/dist/sub-language-registry.d.ts +20 -0
- package/dist/sub-language-registry.d.ts.map +1 -0
- package/dist/sub-language-registry.js +18 -0
- package/dist/sub-language-registry.js.map +1 -0
- package/dist/sub-languages/capture/CompletionProvider.d.ts +32 -0
- package/dist/sub-languages/capture/CompletionProvider.d.ts.map +1 -0
- package/dist/sub-languages/capture/CompletionProvider.js +332 -0
- package/dist/sub-languages/capture/CompletionProvider.js.map +1 -0
- package/dist/sub-languages/capture/HoverProvider.d.ts +8 -0
- package/dist/sub-languages/capture/HoverProvider.d.ts.map +1 -0
- package/dist/sub-languages/capture/HoverProvider.js +87 -0
- package/dist/sub-languages/capture/HoverProvider.js.map +1 -0
- package/dist/sub-languages/capture/Validator.d.ts +6 -0
- package/dist/sub-languages/capture/Validator.d.ts.map +1 -0
- package/dist/sub-languages/capture/Validator.js +72 -0
- package/dist/sub-languages/capture/Validator.js.map +1 -0
- package/dist/sub-languages/capture/index.d.ts +6 -0
- package/dist/sub-languages/capture/index.d.ts.map +1 -0
- package/dist/sub-languages/capture/index.js +58 -0
- package/dist/sub-languages/capture/index.js.map +1 -0
- package/dist/sub-languages/capture/language.d.ts +6 -0
- package/dist/sub-languages/capture/language.d.ts.map +1 -0
- package/dist/sub-languages/capture/language.js +160 -0
- package/dist/sub-languages/capture/language.js.map +1 -0
- package/dist/sub-languages/cdl.d.ts +3 -0
- package/dist/sub-languages/cdl.d.ts.map +1 -0
- package/dist/sub-languages/cdl.js +74 -0
- package/dist/sub-languages/cdl.js.map +1 -0
- package/dist/sub-languages/pdl.d.ts +3 -0
- package/dist/sub-languages/pdl.d.ts.map +1 -0
- package/dist/sub-languages/pdl.js +67 -0
- package/dist/sub-languages/pdl.js.map +1 -0
- package/dist/sub-languages/projection/CodeActionProvider.d.ts +18 -0
- package/dist/sub-languages/projection/CodeActionProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/CodeActionProvider.js +93 -0
- package/dist/sub-languages/projection/CodeActionProvider.js.map +1 -0
- package/dist/sub-languages/projection/CompletionProvider.d.ts +41 -0
- package/dist/sub-languages/projection/CompletionProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/CompletionProvider.js +591 -0
- package/dist/sub-languages/projection/CompletionProvider.js.map +1 -0
- package/dist/sub-languages/projection/HoverProvider.d.ts +24 -0
- package/dist/sub-languages/projection/HoverProvider.d.ts.map +1 -0
- package/dist/sub-languages/projection/HoverProvider.js +298 -0
- package/dist/sub-languages/projection/HoverProvider.js.map +1 -0
- package/dist/sub-languages/projection/Validator.d.ts +22 -0
- package/dist/sub-languages/projection/Validator.d.ts.map +1 -0
- package/dist/sub-languages/projection/Validator.js +339 -0
- package/dist/sub-languages/projection/Validator.js.map +1 -0
- package/dist/sub-languages/projection/index.d.ts +49 -0
- package/dist/sub-languages/projection/index.d.ts.map +1 -0
- package/dist/sub-languages/projection/index.js +160 -0
- package/dist/sub-languages/projection/index.js.map +1 -0
- package/dist/sub-languages/projection/language.d.ts +5 -0
- package/dist/sub-languages/projection/language.d.ts.map +1 -0
- package/dist/sub-languages/projection/language.js +171 -0
- package/dist/sub-languages/projection/language.js.map +1 -0
- package/dist/symbols.d.ts +45 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +100 -0
- package/dist/symbols.js.map +1 -0
- package/dist/themes/screenplay-dark.d.ts +4 -0
- package/dist/themes/screenplay-dark.d.ts.map +1 -0
- package/dist/themes/screenplay-dark.js +29 -0
- package/dist/themes/screenplay-dark.js.map +1 -0
- package/dist/themes/screenplay-light.d.ts +4 -0
- package/dist/themes/screenplay-light.d.ts.map +1 -0
- package/dist/themes/screenplay-light.js +29 -0
- package/dist/themes/screenplay-light.js.map +1 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +121 -0
- package/dist/tokens.js.map +1 -0
- package/dist/validation.d.ts +10 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +101 -0
- package/dist/validation.js.map +1 -0
- package/document-context.ts +51 -0
- package/hover-content.ts +76 -0
- package/hover.ts +23 -0
- package/index.ts +98 -0
- package/keyword-docs.ts +102 -0
- package/language.ts +130 -0
- package/package.json +44 -0
- package/sub-language-registry.ts +42 -0
- package/sub-languages/capture/CompletionProvider.ts +365 -0
- package/sub-languages/capture/HoverProvider.ts +99 -0
- package/sub-languages/capture/Validator.ts +97 -0
- package/sub-languages/capture/index.ts +72 -0
- package/sub-languages/capture/language.ts +176 -0
- package/sub-languages/cdl.ts +76 -0
- package/sub-languages/pdl.ts +69 -0
- package/sub-languages/projection/CodeActionProvider.ts +123 -0
- package/sub-languages/projection/CompletionProvider.ts +653 -0
- package/sub-languages/projection/HoverProvider.ts +338 -0
- package/sub-languages/projection/Validator.ts +387 -0
- package/sub-languages/projection/index.ts +223 -0
- package/sub-languages/projection/language.ts +193 -0
- package/symbols.ts +161 -0
- package/themes/screenplay-dark.ts +32 -0
- package/themes/screenplay-light.ts +32 -0
- package/tokens.ts +145 -0
- package/validation.ts +145 -0
|
@@ -0,0 +1,193 @@
|
|
|
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 type { languages } from 'monaco-editor';
|
|
5
|
+
|
|
6
|
+
export const languageId = 'pdl';
|
|
7
|
+
|
|
8
|
+
// Keywords for the Projection Declaration Language
|
|
9
|
+
const KEYWORDS = [
|
|
10
|
+
'projection',
|
|
11
|
+
'sequence',
|
|
12
|
+
'all',
|
|
13
|
+
'every',
|
|
14
|
+
'on',
|
|
15
|
+
'from',
|
|
16
|
+
'key',
|
|
17
|
+
'parent',
|
|
18
|
+
'join',
|
|
19
|
+
'automap',
|
|
20
|
+
'no',
|
|
21
|
+
'children',
|
|
22
|
+
'nested',
|
|
23
|
+
'identified',
|
|
24
|
+
'by',
|
|
25
|
+
'add',
|
|
26
|
+
'subtract',
|
|
27
|
+
'remove',
|
|
28
|
+
'via',
|
|
29
|
+
'with',
|
|
30
|
+
'default',
|
|
31
|
+
'increment',
|
|
32
|
+
'decrement',
|
|
33
|
+
'count',
|
|
34
|
+
'set',
|
|
35
|
+
'unset',
|
|
36
|
+
'clear',
|
|
37
|
+
'events',
|
|
38
|
+
'id',
|
|
39
|
+
'exclude',
|
|
40
|
+
] as const;
|
|
41
|
+
|
|
42
|
+
// Built-in expressions and functions
|
|
43
|
+
const BUILTINS = [
|
|
44
|
+
'$eventSourceId',
|
|
45
|
+
'$causedBy',
|
|
46
|
+
'$occurred',
|
|
47
|
+
'$namespace',
|
|
48
|
+
'$eventContext',
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
// Operators
|
|
52
|
+
const OPERATORS = ['=', '+=', '-=', '=>', '.'];
|
|
53
|
+
|
|
54
|
+
export const configuration: languages.LanguageConfiguration = {
|
|
55
|
+
comments: {
|
|
56
|
+
lineComment: '#',
|
|
57
|
+
},
|
|
58
|
+
brackets: [
|
|
59
|
+
['{', '}'],
|
|
60
|
+
['[', ']'],
|
|
61
|
+
['(', ')'],
|
|
62
|
+
],
|
|
63
|
+
autoClosingPairs: [
|
|
64
|
+
{ open: '{', close: '}' },
|
|
65
|
+
{ open: '[', close: ']' },
|
|
66
|
+
{ open: '(', close: ')' },
|
|
67
|
+
{ open: '"', close: '"' },
|
|
68
|
+
{ open: "'", close: "'" },
|
|
69
|
+
{ open: '`', close: '`' },
|
|
70
|
+
],
|
|
71
|
+
surroundingPairs: [
|
|
72
|
+
{ open: '{', close: '}' },
|
|
73
|
+
{ open: '[', close: ']' },
|
|
74
|
+
{ open: '(', close: ')' },
|
|
75
|
+
{ open: '"', close: '"' },
|
|
76
|
+
{ open: "'", close: "'" },
|
|
77
|
+
{ open: '`', close: '`' },
|
|
78
|
+
],
|
|
79
|
+
indentationRules: {
|
|
80
|
+
increaseIndentPattern: /^.*(:|\bon\b|\ball\b|\bevery\b|\bchildren\b|\bnested\b|\bparent\b)\s*$/,
|
|
81
|
+
decreaseIndentPattern: /^.*\}.*$/,
|
|
82
|
+
},
|
|
83
|
+
folding: {
|
|
84
|
+
offSide: true,
|
|
85
|
+
markers: {
|
|
86
|
+
start: /^\s*(projection|all|every|on|children|nested|parent)/,
|
|
87
|
+
end: /^\s*$/,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
wordPattern: /@?[a-zA-Z_$][\w$-]*/,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const monarchLanguage: languages.IMonarchLanguage = {
|
|
94
|
+
defaultToken: '',
|
|
95
|
+
tokenPostfix: '.pdl',
|
|
96
|
+
|
|
97
|
+
keywords: KEYWORDS,
|
|
98
|
+
builtins: BUILTINS,
|
|
99
|
+
operators: OPERATORS,
|
|
100
|
+
|
|
101
|
+
// Symbols
|
|
102
|
+
symbols: /[=><!~?:&|+\-*/^%]+/,
|
|
103
|
+
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
|
104
|
+
|
|
105
|
+
tokenizer: {
|
|
106
|
+
root: [
|
|
107
|
+
// Whitespace
|
|
108
|
+
{ include: '@whitespace' },
|
|
109
|
+
|
|
110
|
+
// Template strings with ${...} interpolation
|
|
111
|
+
[/`/, { token: 'string.template', next: '@templateString' }],
|
|
112
|
+
|
|
113
|
+
// Escaped identifiers (@ prefix allows using keywords as identifiers)
|
|
114
|
+
[/@[a-zA-Z_$][\w$]*/, 'identifier.escape'],
|
|
115
|
+
|
|
116
|
+
// Check for uppercase keywords (error case - keywords must be lowercase)
|
|
117
|
+
[
|
|
118
|
+
/[A-Z][a-zA-Z_$][\w$]*/,
|
|
119
|
+
{
|
|
120
|
+
cases: {
|
|
121
|
+
'@keywords': 'invalid',
|
|
122
|
+
'@default': 'identifier',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
|
|
127
|
+
// Identifiers and keywords (lowercase only for keywords)
|
|
128
|
+
[
|
|
129
|
+
/[a-z_$][\w$]*/,
|
|
130
|
+
{
|
|
131
|
+
cases: {
|
|
132
|
+
'@keywords': 'keyword',
|
|
133
|
+
'@builtins': 'variable.predefined',
|
|
134
|
+
'@default': 'identifier',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
|
|
139
|
+
// Delimiters and operators
|
|
140
|
+
[/[{}()[\]]/, '@brackets'],
|
|
141
|
+
[/=>/, 'operator.arrow'],
|
|
142
|
+
[
|
|
143
|
+
/@symbols/,
|
|
144
|
+
{
|
|
145
|
+
cases: {
|
|
146
|
+
'@operators': 'operator',
|
|
147
|
+
'@default': '',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
|
|
152
|
+
// Numbers
|
|
153
|
+
[/\d*\.\d+([eE][-+]?\d+)?/, 'number.float'],
|
|
154
|
+
[/0[xX][0-9a-fA-F]+/, 'number.hex'],
|
|
155
|
+
[/\d+/, 'number'],
|
|
156
|
+
|
|
157
|
+
// Delimiter: after number because of .\d floats
|
|
158
|
+
[/[;,.]/, 'delimiter'],
|
|
159
|
+
|
|
160
|
+
// Strings
|
|
161
|
+
[/"([^"\\]|\\.)*$/, 'string.invalid'],
|
|
162
|
+
[/"/, { token: 'string.quote', next: '@string' }],
|
|
163
|
+
],
|
|
164
|
+
|
|
165
|
+
whitespace: [
|
|
166
|
+
[/[ \t\r\n]+/, ''],
|
|
167
|
+
[/#.*$/, 'comment'],
|
|
168
|
+
],
|
|
169
|
+
|
|
170
|
+
// Template string state
|
|
171
|
+
templateString: [
|
|
172
|
+
[/\$\{/, { token: 'delimiter.bracket', next: '@templateExpression' }],
|
|
173
|
+
[/[^\\`$]+/, 'string.template'],
|
|
174
|
+
[/@escapes/, 'string.template.escape'],
|
|
175
|
+
[/\\./, 'string.template.escape.invalid'],
|
|
176
|
+
[/`/, { token: 'string.template', next: '@pop' }],
|
|
177
|
+
],
|
|
178
|
+
|
|
179
|
+
// Template expression inside ${}
|
|
180
|
+
templateExpression: [
|
|
181
|
+
[/\}/, { token: 'delimiter.bracket', next: '@pop' }],
|
|
182
|
+
{ include: '@root' },
|
|
183
|
+
],
|
|
184
|
+
|
|
185
|
+
// Regular string state
|
|
186
|
+
string: [
|
|
187
|
+
[/[^\\"]+/, 'string'],
|
|
188
|
+
[/@escapes/, 'string.escape'],
|
|
189
|
+
[/\\./, 'string.escape.invalid'],
|
|
190
|
+
[/"/, { token: 'string.quote', next: '@pop' }],
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
};
|
package/symbols.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
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 { fenceMap, indentOf } from './document-context';
|
|
5
|
+
|
|
6
|
+
export interface PropertySymbol {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ConceptSymbol {
|
|
12
|
+
name: string;
|
|
13
|
+
primitive: string;
|
|
14
|
+
attributes: string[];
|
|
15
|
+
enumValues: string[];
|
|
16
|
+
line: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PolicySymbol {
|
|
20
|
+
name: string;
|
|
21
|
+
requires: string[];
|
|
22
|
+
line: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface EventSymbol {
|
|
26
|
+
name: string;
|
|
27
|
+
properties: PropertySymbol[];
|
|
28
|
+
line: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface NamedSymbol {
|
|
32
|
+
name: string;
|
|
33
|
+
line: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface QuerySymbol extends NamedSymbol {
|
|
37
|
+
returnType: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ImportSymbol {
|
|
41
|
+
qualifiedName: string;
|
|
42
|
+
shortName: string;
|
|
43
|
+
line: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DocumentSymbols {
|
|
47
|
+
imports: ImportSymbol[];
|
|
48
|
+
concepts: ConceptSymbol[];
|
|
49
|
+
policies: PolicySymbol[];
|
|
50
|
+
events: EventSymbol[];
|
|
51
|
+
commands: NamedSymbol[];
|
|
52
|
+
queries: QuerySymbol[];
|
|
53
|
+
screens: NamedSymbol[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const conceptPattern = /^concept\s+(\w+)\s*:\s*(\w+)((?:\s+@\w+)*)\s*$/;
|
|
57
|
+
const propertyPattern = /^\s*([a-z_]\w*)\s+([\w.]+(?:\[\])?\??)\s*$/;
|
|
58
|
+
|
|
59
|
+
function collectBody(lines: string[], fences: boolean[], start: number, indent: number): number[] {
|
|
60
|
+
const body: number[] = [];
|
|
61
|
+
for (let index = start + 1; index < lines.length; index++) {
|
|
62
|
+
if (fences[index]) {
|
|
63
|
+
body.push(index);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const line = lines[index];
|
|
67
|
+
if (line.trim().length === 0) continue;
|
|
68
|
+
if (indentOf(line) <= indent) break;
|
|
69
|
+
body.push(index);
|
|
70
|
+
}
|
|
71
|
+
return body;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function scanDocument(lines: string[]): DocumentSymbols {
|
|
75
|
+
const symbols: DocumentSymbols = {
|
|
76
|
+
imports: [],
|
|
77
|
+
concepts: [],
|
|
78
|
+
policies: [],
|
|
79
|
+
events: [],
|
|
80
|
+
commands: [],
|
|
81
|
+
queries: [],
|
|
82
|
+
screens: [],
|
|
83
|
+
};
|
|
84
|
+
const fences = fenceMap(lines);
|
|
85
|
+
|
|
86
|
+
for (let index = 0; index < lines.length; index++) {
|
|
87
|
+
if (fences[index]) continue;
|
|
88
|
+
const line = lines[index];
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
const indent = indentOf(line);
|
|
91
|
+
|
|
92
|
+
const importMatch = trimmed.match(/^import\s+([\w.]+)\s*$/);
|
|
93
|
+
if (importMatch && indent === 0) {
|
|
94
|
+
const qualifiedName = importMatch[1];
|
|
95
|
+
const shortName = qualifiedName.split('.').pop() ?? qualifiedName;
|
|
96
|
+
symbols.imports.push({ qualifiedName, shortName, line: index });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const conceptMatch = trimmed.match(conceptPattern);
|
|
101
|
+
if (conceptMatch) {
|
|
102
|
+
const enumValues =
|
|
103
|
+
conceptMatch[2] === 'Enum'
|
|
104
|
+
? collectBody(lines, fences, index, indent).map((i) => lines[i].trim())
|
|
105
|
+
: [];
|
|
106
|
+
symbols.concepts.push({
|
|
107
|
+
name: conceptMatch[1],
|
|
108
|
+
primitive: conceptMatch[2],
|
|
109
|
+
attributes: conceptMatch[3].trim().split(/\s+/).filter(Boolean),
|
|
110
|
+
enumValues,
|
|
111
|
+
line: index,
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const policyMatch = trimmed.match(/^policy\s+(\w+)\s*$/);
|
|
117
|
+
if (policyMatch) {
|
|
118
|
+
const requires = collectBody(lines, fences, index, indent)
|
|
119
|
+
.filter((i) => !fences[i])
|
|
120
|
+
.map((i) => lines[i].trim());
|
|
121
|
+
symbols.policies.push({ name: policyMatch[1], requires, line: index });
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const eventMatch = trimmed.match(/^event\s+(\w+)\s*$/);
|
|
126
|
+
if (eventMatch) {
|
|
127
|
+
const properties = collectBody(lines, fences, index, indent)
|
|
128
|
+
.map((i) => lines[i].match(propertyPattern))
|
|
129
|
+
.filter((match): match is RegExpMatchArray => match !== null)
|
|
130
|
+
.map((match) => ({ name: match[1], type: match[2] }));
|
|
131
|
+
symbols.events.push({ name: eventMatch[1], properties, line: index });
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const commandMatch = trimmed.match(/^command\s+(\w+)\s*$/);
|
|
136
|
+
if (commandMatch) {
|
|
137
|
+
symbols.commands.push({ name: commandMatch[1], line: index });
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const queryMatch = trimmed.match(/^query\s+(\w+)\s*=>\s*([\w[\]]+)\s*$/);
|
|
142
|
+
if (queryMatch) {
|
|
143
|
+
symbols.queries.push({ name: queryMatch[1], returnType: queryMatch[2], line: index });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const screenMatch = trimmed.match(/^screen\s+(\w+)\s*$/);
|
|
148
|
+
if (screenMatch) {
|
|
149
|
+
symbols.screens.push({ name: screenMatch[1], line: index });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return symbols;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function knownEventNames(symbols: DocumentSymbols): string[] {
|
|
157
|
+
return [
|
|
158
|
+
...symbols.events.map((event) => event.name),
|
|
159
|
+
...symbols.imports.map((imported) => imported.shortName),
|
|
160
|
+
];
|
|
161
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 type { editor } from 'monaco-editor';
|
|
5
|
+
|
|
6
|
+
export const screenplayDarkThemeName = 'screenplay-dark';
|
|
7
|
+
|
|
8
|
+
export const screenplayDark: editor.IStandaloneThemeData = {
|
|
9
|
+
base: 'vs-dark',
|
|
10
|
+
inherit: true,
|
|
11
|
+
rules: [
|
|
12
|
+
{ token: 'keyword.play', foreground: 'C586C0' },
|
|
13
|
+
{ token: 'keyword.tag.play', foreground: '569CD6', fontStyle: 'italic' },
|
|
14
|
+
{ token: 'keyword.type.play', foreground: '4FC1FF', fontStyle: 'bold' },
|
|
15
|
+
{ token: 'type.play', foreground: '4EC9B0' },
|
|
16
|
+
{ token: 'type.identifier.play', foreground: '4EC9B0' },
|
|
17
|
+
{ token: 'identifier.play', foreground: '9CDCFE' },
|
|
18
|
+
{ token: 'annotation.play', foreground: 'DCDCAA' },
|
|
19
|
+
{ token: 'variable.predefined.play', foreground: 'D7BA7D' },
|
|
20
|
+
{ token: 'string.play', foreground: 'CE9178' },
|
|
21
|
+
{ token: 'string.invalid.play', foreground: 'F44747' },
|
|
22
|
+
{ token: 'string.quote.play', foreground: '6A9955' },
|
|
23
|
+
{ token: 'number.play', foreground: 'B5CEA8' },
|
|
24
|
+
{ token: 'number.float.play', foreground: 'B5CEA8' },
|
|
25
|
+
{ token: 'comment.play', foreground: '6A9955', fontStyle: 'italic' },
|
|
26
|
+
{ token: 'operator.play', foreground: 'D4D4D4' },
|
|
27
|
+
{ token: 'delimiter.play', foreground: 'CCCCCC' },
|
|
28
|
+
],
|
|
29
|
+
colors: {
|
|
30
|
+
'editor.background': '#1E1E1E',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
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 type { editor } from 'monaco-editor';
|
|
5
|
+
|
|
6
|
+
export const screenplayLightThemeName = 'screenplay-light';
|
|
7
|
+
|
|
8
|
+
export const screenplayLight: editor.IStandaloneThemeData = {
|
|
9
|
+
base: 'vs',
|
|
10
|
+
inherit: true,
|
|
11
|
+
rules: [
|
|
12
|
+
{ token: 'keyword.play', foreground: 'AF00DB' },
|
|
13
|
+
{ token: 'keyword.tag.play', foreground: '0000FF', fontStyle: 'italic' },
|
|
14
|
+
{ token: 'keyword.type.play', foreground: '0070C1', fontStyle: 'bold' },
|
|
15
|
+
{ token: 'type.play', foreground: '267F99' },
|
|
16
|
+
{ token: 'type.identifier.play', foreground: '267F99' },
|
|
17
|
+
{ token: 'identifier.play', foreground: '001080' },
|
|
18
|
+
{ token: 'annotation.play', foreground: '795E26' },
|
|
19
|
+
{ token: 'variable.predefined.play', foreground: 'B45309' },
|
|
20
|
+
{ token: 'string.play', foreground: 'A31515' },
|
|
21
|
+
{ token: 'string.invalid.play', foreground: 'CD3131' },
|
|
22
|
+
{ token: 'string.quote.play', foreground: '008000' },
|
|
23
|
+
{ token: 'number.play', foreground: '098658' },
|
|
24
|
+
{ token: 'number.float.play', foreground: '098658' },
|
|
25
|
+
{ token: 'comment.play', foreground: '008000', fontStyle: 'italic' },
|
|
26
|
+
{ token: 'operator.play', foreground: '000000' },
|
|
27
|
+
{ token: 'delimiter.play', foreground: '000000' },
|
|
28
|
+
],
|
|
29
|
+
colors: {
|
|
30
|
+
'editor.background': '#FFFFFF',
|
|
31
|
+
},
|
|
32
|
+
};
|
package/tokens.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 type { languages } from 'monaco-editor';
|
|
5
|
+
import {
|
|
6
|
+
clauseKeywords,
|
|
7
|
+
codeBlockTags,
|
|
8
|
+
constructKeywords,
|
|
9
|
+
primitiveTypes,
|
|
10
|
+
sliceTypes,
|
|
11
|
+
} from './language';
|
|
12
|
+
import { MonarchTokenRules, SubLanguage } from './sub-language-registry';
|
|
13
|
+
|
|
14
|
+
// Maps a Screenplay inline code tag to the Monaco language id used for embedded highlighting.
|
|
15
|
+
const embeddedLanguages: Record<string, string> = {
|
|
16
|
+
csharp: 'csharp',
|
|
17
|
+
typescript: 'typescript',
|
|
18
|
+
react: 'typescript',
|
|
19
|
+
html: 'html',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function subLanguageState(keyword: string): string {
|
|
23
|
+
return `subLanguage_${keyword}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// A construct keyword at the start of a line ends any indented sub-language block.
|
|
27
|
+
function subLanguageExitRule(subLanguages: SubLanguage[]): MonarchTokenRules[number] {
|
|
28
|
+
const exitKeywords = [...constructKeywords, ...subLanguages.map((subLanguage) => subLanguage.keyword)];
|
|
29
|
+
return [
|
|
30
|
+
new RegExp(`^\\s*(?:${exitKeywords.join('|')})\\b`),
|
|
31
|
+
{ token: '@rematch', next: '@pop' },
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Shared across the composite `.play` tokenizer and every standalone sub-language tokenizer
|
|
36
|
+
// (comments, `$`-prefixed context variables, strings, numbers, operators, delimiters, whitespace).
|
|
37
|
+
export const commonTokenRules: MonarchTokenRules = [
|
|
38
|
+
[/\/\/.*$/, 'comment'],
|
|
39
|
+
[/\$(?:context|eventContext|eventSourceId|causedBy)(?:\.\w+)*/, 'variable.predefined'],
|
|
40
|
+
[/\$(?:env|secrets|strings)\.[\w.]+/, 'variable.predefined'],
|
|
41
|
+
[/\$\.[\w.]*/, 'variable.predefined'],
|
|
42
|
+
[/"(?:[^"\\]|\\.)*"/, 'string'],
|
|
43
|
+
[/"(?:[^"\\]|\\.)*$/, 'string.invalid'],
|
|
44
|
+
[/\d+(?:ms|s|m|h|d)\b/, 'number'],
|
|
45
|
+
[/-?\d+\.\d+/, 'number.float'],
|
|
46
|
+
[/-?\d+/, 'number'],
|
|
47
|
+
[/=>|==|!=|>=|<=|[><=:?]/, 'operator'],
|
|
48
|
+
[/[[\](),.]/, 'delimiter'],
|
|
49
|
+
[/\s+/, 'white'],
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export function createTokensProvider(subLanguages: SubLanguage[]): languages.IMonarchLanguage {
|
|
53
|
+
const tokenizer: Record<string, MonarchTokenRules> = {
|
|
54
|
+
root: [
|
|
55
|
+
// Inline code block tags at end of line open an embedded code block.
|
|
56
|
+
...codeBlockTags.map(
|
|
57
|
+
(tag): MonarchTokenRules[number] => [
|
|
58
|
+
new RegExp(`\\b${tag}\\b(?=\\s*$)`),
|
|
59
|
+
{ token: 'keyword.tag', next: `@codeBlockPending.${embeddedLanguages[tag]}` },
|
|
60
|
+
],
|
|
61
|
+
),
|
|
62
|
+
// Sub-language constructs switch to the registered sub-language's token rules.
|
|
63
|
+
...subLanguages.map(
|
|
64
|
+
(subLanguage): MonarchTokenRules[number] => [
|
|
65
|
+
new RegExp(`\\b${subLanguage.keyword}\\b`),
|
|
66
|
+
{ token: 'keyword', next: `@${subLanguageState(subLanguage.keyword)}` },
|
|
67
|
+
],
|
|
68
|
+
),
|
|
69
|
+
// A bare description keyword at end of line opens a fenced plain-text block.
|
|
70
|
+
[/\bdescription\b(?=\s*$)/, { token: 'keyword', next: '@descriptionBlockPending' }],
|
|
71
|
+
[/\brow-click\b/, 'keyword'],
|
|
72
|
+
[/@\w+/, 'annotation'],
|
|
73
|
+
[
|
|
74
|
+
/[A-Z]\w*/,
|
|
75
|
+
{
|
|
76
|
+
cases: {
|
|
77
|
+
'@sliceTypes': 'keyword.type',
|
|
78
|
+
'@primitiveTypes': 'type',
|
|
79
|
+
'@default': 'type.identifier',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
/[a-z_]\w*/,
|
|
85
|
+
{
|
|
86
|
+
cases: {
|
|
87
|
+
'@keywords': 'keyword',
|
|
88
|
+
'@default': 'identifier',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
{ include: '@common' },
|
|
93
|
+
],
|
|
94
|
+
|
|
95
|
+
common: commonTokenRules,
|
|
96
|
+
|
|
97
|
+
// After a code tag, the only thing allowed before the opening fence is whitespace.
|
|
98
|
+
codeBlockPending: [
|
|
99
|
+
[
|
|
100
|
+
/^\s*```\s*$/,
|
|
101
|
+
{ token: 'string.quote', switchTo: '@codeBlock.$S2', nextEmbedded: '$S2' },
|
|
102
|
+
],
|
|
103
|
+
[/^\s*[^\s`].*$/, { token: '@rematch', next: '@pop' }],
|
|
104
|
+
[/\s+/, 'white'],
|
|
105
|
+
],
|
|
106
|
+
|
|
107
|
+
codeBlock: [
|
|
108
|
+
[/^\s*```\s*$/, { token: 'string.quote', next: '@pop', nextEmbedded: '@pop' }],
|
|
109
|
+
[/[^`]+/, ''],
|
|
110
|
+
[/`/, ''],
|
|
111
|
+
],
|
|
112
|
+
|
|
113
|
+
// After a bare description, the only thing allowed before the opening fence is whitespace.
|
|
114
|
+
descriptionBlockPending: [
|
|
115
|
+
[/^\s*```\s*$/, { token: 'string.quote', switchTo: '@descriptionBlock' }],
|
|
116
|
+
[/^\s*[^\s`].*$/, { token: '@rematch', next: '@pop' }],
|
|
117
|
+
[/\s+/, 'white'],
|
|
118
|
+
],
|
|
119
|
+
|
|
120
|
+
descriptionBlock: [
|
|
121
|
+
[/^\s*```\s*$/, { token: 'string.quote', next: '@pop' }],
|
|
122
|
+
[/.+/, 'string'],
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
for (const subLanguage of subLanguages) {
|
|
127
|
+
tokenizer[subLanguageState(subLanguage.keyword)] = [
|
|
128
|
+
subLanguageExitRule(subLanguages),
|
|
129
|
+
...subLanguage.tokens,
|
|
130
|
+
{ include: '@common' },
|
|
131
|
+
[/[A-Z]\w*/, 'type.identifier'],
|
|
132
|
+
[/[a-z_]\w*/, 'identifier'],
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
defaultToken: '',
|
|
138
|
+
tokenPostfix: '.play',
|
|
139
|
+
ignoreCase: false,
|
|
140
|
+
keywords: [...constructKeywords, ...clauseKeywords, ...codeBlockTags],
|
|
141
|
+
sliceTypes,
|
|
142
|
+
primitiveTypes,
|
|
143
|
+
tokenizer,
|
|
144
|
+
} as languages.IMonarchLanguage;
|
|
145
|
+
}
|