@cratis/screenplay-language 4.1.0 → 4.2.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.
@@ -0,0 +1,11 @@
1
+ export declare const fileReferenceKeyword = "file";
2
+ export interface FileReference {
3
+ path: string;
4
+ line: number;
5
+ startColumn: number;
6
+ endColumn: number;
7
+ }
8
+ export declare function isAbsoluteFileReferencePath(path: string): boolean;
9
+ export declare function fileReferenceOn(line: string, lineIndex: number): FileReference | undefined;
10
+ export declare function fileReferences(lines: string[]): FileReference[];
11
+ //# sourceMappingURL=file-references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-references.d.ts","sourceRoot":"","sources":["../file-references.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAK3C,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE;AA0BD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAe1F;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAW/D"}
@@ -0,0 +1,70 @@
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
+ import { fenceMap, firstWord, indentOf } from './document-context';
4
+ // The keyword the directive opens with — the single word the language points at a file with.
5
+ export const fileReferenceKeyword = 'file';
6
+ // `file String` is a property named `file`; `file Invoices/Register.cs` is the directive. The two are
7
+ // told apart by shape, the way the compiler tells them apart: a type reference is a bare identifier,
8
+ // so anything carrying a separator or an extension is a path and nothing else. The property wins the
9
+ // tie, because a document that used the name before the directive existed keeps meaning what it meant.
10
+ const typeReferencePattern = /^[A-Za-z_]\w*(?:\[\])?\??$/;
11
+ // A path naming a place on one machine rather than a place in the repository. The compiler warns about
12
+ // these; nothing here rejects one, so a resolver can still probe it and come up empty on other machines.
13
+ const absolutePathPattern = /^(?:[/\\]|[A-Za-z]:[/\\]|~[/\\])/;
14
+ export function isAbsoluteFileReferencePath(path) {
15
+ return absolutePathPattern.test(path);
16
+ }
17
+ // Drops a trailing `//` comment without cutting into a string or a template, mirroring how the
18
+ // compiler strips one before it reads the line.
19
+ function withoutComment(text) {
20
+ let inString = false;
21
+ let inTemplate = false;
22
+ for (let index = 0; index < text.length; index++) {
23
+ const current = text[index];
24
+ if (current === '\\' && inString && index + 1 < text.length) {
25
+ index++;
26
+ }
27
+ else if (current === '"' && !inTemplate) {
28
+ inString = !inString;
29
+ }
30
+ else if (current === '`' && !inString) {
31
+ inTemplate = !inTemplate;
32
+ }
33
+ else if (!inString && !inTemplate && current === '/' && text[index + 1] === '/') {
34
+ return text.slice(0, index);
35
+ }
36
+ }
37
+ return text;
38
+ }
39
+ // The `file` directive on a single line, or undefined when the line is not one. Callers that scan a
40
+ // whole document go through fileReferences instead, so code fences are accounted for.
41
+ export function fileReferenceOn(line, lineIndex) {
42
+ const indent = indentOf(line);
43
+ const content = withoutComment(line.slice(indent)).trimEnd();
44
+ if (firstWord(content) !== fileReferenceKeyword)
45
+ return undefined;
46
+ const afterKeyword = content.slice(fileReferenceKeyword.length);
47
+ const path = afterKeyword.trim();
48
+ // A bare `file` leaves the word available as a name to whatever the enclosing block reads a bare
49
+ // word as, and a bare identifier after it is a property type rather than a path.
50
+ if (path.length === 0 || typeReferencePattern.test(path))
51
+ return undefined;
52
+ const separatorLength = afterKeyword.length - afterKeyword.trimStart().length;
53
+ const startColumn = indent + fileReferenceKeyword.length + separatorLength + 1;
54
+ return { path, line: lineIndex, startColumn, endColumn: startColumn + path.length };
55
+ }
56
+ // Every `file` directive in a document, in document order. Lines inside a code fence are skipped, so
57
+ // a `file` line in an inline C# or TypeScript block is never mistaken for one of ours.
58
+ export function fileReferences(lines) {
59
+ const fences = fenceMap(lines);
60
+ const references = [];
61
+ for (let index = 0; index < lines.length; index++) {
62
+ if (fences[index])
63
+ continue;
64
+ const reference = fileReferenceOn(lines[index], index);
65
+ if (reference)
66
+ references.push(reference);
67
+ }
68
+ return references;
69
+ }
70
+ //# sourceMappingURL=file-references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-references.js","sourceRoot":"","sources":["../file-references.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,8 @@ export { cdl } from './sub-languages/cdl';
10
10
  export { screenplayDarkThemeName } from './themes/screenplay-dark';
11
11
  export { screenplayLightThemeName } from './themes/screenplay-light';
12
12
  export { enclosingChain, fenceMap, firstWord, indentOf } from './document-context';
13
+ export { fileReferenceKeyword, fileReferenceOn, fileReferences, isAbsoluteFileReferencePath, } from './file-references';
14
+ export type { FileReference } from './file-references';
13
15
  export { builtInTriggerNames, knownEventNames, knownTriggerNames, knownTypeNames, scanDocument } from './symbols';
14
16
  export type { CommandSymbol, ConceptSymbol, DocumentSymbols, EventSymbol, ImportSymbol, NamedSymbol, PolicySymbol, PropertySymbol, QuerySymbol, TypeSymbol, } from './symbols';
15
17
  export { attributeDocs, contextVariableDocs, keywordDocs } from './keyword-docs';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,MAAM,EAIT,MAAM,YAAY,CAAC;AA2BpB,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAe7C;AAED,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EACR,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,GACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAClH,YAAY,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,MAAM,EAIT,MAAM,YAAY,CAAC;AA2BpB,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAe7C;AAED,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EACR,iBAAiB,EACjB,WAAW,EACX,qBAAqB,EACrB,qBAAqB,GACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EACH,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,2BAA2B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAClH,YAAY,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC7E,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ export { cdl } from './sub-languages/cdl';
45
45
  export { screenplayDarkThemeName } from './themes/screenplay-dark';
46
46
  export { screenplayLightThemeName } from './themes/screenplay-light';
47
47
  export { enclosingChain, fenceMap, firstWord, indentOf } from './document-context';
48
+ export { fileReferenceKeyword, fileReferenceOn, fileReferences, isAbsoluteFileReferencePath, } from './file-references';
48
49
  export { builtInTriggerNames, knownEventNames, knownTriggerNames, knownTypeNames, scanDocument } from './symbols';
49
50
  export { attributeDocs, contextVariableDocs, keywordDocs } from './keyword-docs';
50
51
  export { contextVariableItems, producesItems } from './completion-items';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qGAAqG;AAErG,OAAO,EAEH,qBAAqB,EACrB,sBAAsB,EACtB,UAAU,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACH,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,mBAAmB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE9C,SAAS,mBAAmB,CAAC,MAAc;IACvC,MAAM,CAAC,SAAS,CAAC,wBAAwB,CAAC,UAAU,EAAE,oBAAoB,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,gFAAgF;AAChF,qBAAqB,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAE9E,iFAAiF;AACjF,mFAAmF;AACnF,MAAM,UAAU,yBAAyB;IACrC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC;QAAE,mBAAmB,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IAC1E,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;QAAE,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc;IACnC,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO;IAE5C,yBAAyB,EAAE,CAAC;IAE5B,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEhC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;IAClD,MAAM,CAAC,SAAS,CAAC,wBAAwB,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;IAC7E,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,UAAU,EAAE,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9F,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC1E,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,uBAAuB,EAAE,cAAc,CAAC,CAAC;IACnE,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,wBAAwB,EAAE,eAAe,CAAC,CAAC;IACrE,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,OAAO,EACH,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,cAAc,EACd,UAAU,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAO/F,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAalH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
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, firstWord, indentOf } from './document-context';
5
+
6
+ // The keyword the directive opens with — the single word the language points at a file with.
7
+ export const fileReferenceKeyword = 'file';
8
+
9
+ // A `file <path>` directive, with the path exactly as written and the span it occupies on its line.
10
+ // The span is measured against the raw line, and its columns are one-based like every other position
11
+ // this package reports, so an editor places a range on it the same way it does a validation issue.
12
+ export interface FileReference {
13
+ path: string;
14
+ line: number;
15
+ startColumn: number;
16
+ endColumn: number;
17
+ }
18
+
19
+ // `file String` is a property named `file`; `file Invoices/Register.cs` is the directive. The two are
20
+ // told apart by shape, the way the compiler tells them apart: a type reference is a bare identifier,
21
+ // so anything carrying a separator or an extension is a path and nothing else. The property wins the
22
+ // tie, because a document that used the name before the directive existed keeps meaning what it meant.
23
+ const typeReferencePattern = /^[A-Za-z_]\w*(?:\[\])?\??$/;
24
+
25
+ // A path naming a place on one machine rather than a place in the repository. The compiler warns about
26
+ // these; nothing here rejects one, so a resolver can still probe it and come up empty on other machines.
27
+ const absolutePathPattern = /^(?:[/\\]|[A-Za-z]:[/\\]|~[/\\])/;
28
+
29
+ export function isAbsoluteFileReferencePath(path: string): boolean {
30
+ return absolutePathPattern.test(path);
31
+ }
32
+
33
+ // Drops a trailing `//` comment without cutting into a string or a template, mirroring how the
34
+ // compiler strips one before it reads the line.
35
+ function withoutComment(text: string): string {
36
+ let inString = false;
37
+ let inTemplate = false;
38
+
39
+ for (let index = 0; index < text.length; index++) {
40
+ const current = text[index];
41
+ if (current === '\\' && inString && index + 1 < text.length) {
42
+ index++;
43
+ } else if (current === '"' && !inTemplate) {
44
+ inString = !inString;
45
+ } else if (current === '`' && !inString) {
46
+ inTemplate = !inTemplate;
47
+ } else if (!inString && !inTemplate && current === '/' && text[index + 1] === '/') {
48
+ return text.slice(0, index);
49
+ }
50
+ }
51
+
52
+ return text;
53
+ }
54
+
55
+ // The `file` directive on a single line, or undefined when the line is not one. Callers that scan a
56
+ // whole document go through fileReferences instead, so code fences are accounted for.
57
+ export function fileReferenceOn(line: string, lineIndex: number): FileReference | undefined {
58
+ const indent = indentOf(line);
59
+ const content = withoutComment(line.slice(indent)).trimEnd();
60
+ if (firstWord(content) !== fileReferenceKeyword) return undefined;
61
+
62
+ const afterKeyword = content.slice(fileReferenceKeyword.length);
63
+ const path = afterKeyword.trim();
64
+
65
+ // A bare `file` leaves the word available as a name to whatever the enclosing block reads a bare
66
+ // word as, and a bare identifier after it is a property type rather than a path.
67
+ if (path.length === 0 || typeReferencePattern.test(path)) return undefined;
68
+
69
+ const separatorLength = afterKeyword.length - afterKeyword.trimStart().length;
70
+ const startColumn = indent + fileReferenceKeyword.length + separatorLength + 1;
71
+ return { path, line: lineIndex, startColumn, endColumn: startColumn + path.length };
72
+ }
73
+
74
+ // Every `file` directive in a document, in document order. Lines inside a code fence are skipped, so
75
+ // a `file` line in an inline C# or TypeScript block is never mistaken for one of ours.
76
+ export function fileReferences(lines: string[]): FileReference[] {
77
+ const fences = fenceMap(lines);
78
+ const references: FileReference[] = [];
79
+
80
+ for (let index = 0; index < lines.length; index++) {
81
+ if (fences[index]) continue;
82
+ const reference = fileReferenceOn(lines[index], index);
83
+ if (reference) references.push(reference);
84
+ }
85
+
86
+ return references;
87
+ }
@@ -0,0 +1,26 @@
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 { describe, it } from 'vitest';
5
+ import { isAbsoluteFileReferencePath } from '../../file-references';
6
+
7
+ // A reference is relative to the repository root, so it means the same thing on every machine. An
8
+ // absolute one names a place on one machine and is wrong without looking anything up.
9
+ describe('when recognizing an absolute path', () => {
10
+ it('should recognize a posix path', () => {
11
+ isAbsoluteFileReferencePath('/Users/someone/Invoices/Register.cs').should.be.true;
12
+ });
13
+
14
+ it('should recognize a windows path', () => {
15
+ isAbsoluteFileReferencePath('C:/repos/Invoices/Register.cs').should.be.true;
16
+ isAbsoluteFileReferencePath('C:\\repos\\Invoices\\Register.cs').should.be.true;
17
+ });
18
+
19
+ it('should recognize a home-relative path', () => {
20
+ isAbsoluteFileReferencePath('~/repos/Invoices/Register.cs').should.be.true;
21
+ });
22
+
23
+ it('should not recognize a repository-relative path', () => {
24
+ isAbsoluteFileReferencePath('Invoices/Register.cs').should.be.false;
25
+ });
26
+ });
@@ -0,0 +1,26 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ // An inline C# block may well contain the word `file` at the start of a line — it is a C# modifier —
8
+ // and none of it is Screenplay.
9
+ describe('when scanning a document with a file line inside a code fence', () => {
10
+ let result: FileReference[];
11
+
12
+ beforeEach(() => {
13
+ result = fileReferences([
14
+ 'handler',
15
+ ' ```',
16
+ ' file static class Helpers/NotAPath.cs',
17
+ ' ```',
18
+ ' file Handlers/Real.cs',
19
+ ]);
20
+ });
21
+
22
+ it('should find only the reference outside the fence', () => {
23
+ result.should.have.lengthOf(1);
24
+ result[0].path.should.equal('Handlers/Real.cs');
25
+ });
26
+ });
@@ -0,0 +1,19 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ // A bare `file` carries no path, so it leaves the word available as a name to whatever the enclosing
8
+ // block reads a bare word as.
9
+ describe('when scanning a document with a bare file keyword', () => {
10
+ let result: FileReference[];
11
+
12
+ beforeEach(() => {
13
+ result = fileReferences(['reaction', ' file', ' file ', 'filename Something.cs']);
14
+ });
15
+
16
+ it('should find no reference', () => {
17
+ result.should.be.empty;
18
+ });
19
+ });
@@ -0,0 +1,30 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ describe('when scanning a document with a file directive', () => {
8
+ let result: FileReference[];
9
+
10
+ beforeEach(() => {
11
+ result = fileReferences(['command RegisterInvoice', ' handler', ' file Handlers/RegisterInvoice.cs']);
12
+ });
13
+
14
+ it('should find the reference', () => {
15
+ result.should.have.lengthOf(1);
16
+ });
17
+
18
+ it('should carry the declared path verbatim', () => {
19
+ result[0].path.should.equal('Handlers/RegisterInvoice.cs');
20
+ });
21
+
22
+ it('should report the line it sits on', () => {
23
+ result[0].line.should.equal(2);
24
+ });
25
+
26
+ it('should span the path and not the keyword', () => {
27
+ result[0].startColumn.should.equal(14);
28
+ result[0].endColumn.should.equal(14 + 'Handlers/RegisterInvoice.cs'.length);
29
+ });
30
+ });
@@ -0,0 +1,25 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ // `file String` declares a property called `file`; only a path is the directive. The two are told
8
+ // apart by shape, and the property has to keep winning the tie.
9
+ describe('when scanning a document with a property named file', () => {
10
+ let result: FileReference[];
11
+
12
+ beforeEach(() => {
13
+ result = fileReferences([
14
+ 'type Attachment',
15
+ ' file String',
16
+ ' name String',
17
+ ' thumbnails String[]',
18
+ ' caption String?',
19
+ ]);
20
+ });
21
+
22
+ it('should find no reference', () => {
23
+ result.should.be.empty;
24
+ });
25
+ });
@@ -0,0 +1,25 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ describe('when scanning a document with a trailing comment after a file directive', () => {
8
+ let result: FileReference[];
9
+
10
+ beforeEach(() => {
11
+ result = fileReferences([' file Screens/InvoiceLineReport.tsx // generated by Arc']);
12
+ });
13
+
14
+ it('should find the reference', () => {
15
+ result.should.have.lengthOf(1);
16
+ });
17
+
18
+ it('should leave the comment out of the path', () => {
19
+ result[0].path.should.equal('Screens/InvoiceLineReport.tsx');
20
+ });
21
+
22
+ it('should span only the path', () => {
23
+ result[0].endColumn.should.equal(result[0].startColumn + 'Screens/InvoiceLineReport.tsx'.length);
24
+ });
25
+ });
@@ -0,0 +1,41 @@
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 { describe, beforeEach, it } from 'vitest';
5
+ import { FileReference, fileReferences } from '../../file-references';
6
+
7
+ // The shape a captured application actually has — the directive appears under validation rules,
8
+ // constraints, handlers, screens, performers and reactions alike, at whatever depth they sit.
9
+ describe('when scanning a document with directives on several constructs', () => {
10
+ let result: FileReference[];
11
+
12
+ beforeEach(() => {
13
+ result = fileReferences([
14
+ 'rule BeUnusedInvoiceNumber',
15
+ ' file Validations/BeUnusedInvoiceNumber.cs',
16
+ 'constraint InvoiceStatusTransition',
17
+ ' file Constraints/InvoiceStatusTransitionConstraint.cs',
18
+ 'screen InvoiceLineReport',
19
+ ' file Screens/InvoiceLineReport.tsx',
20
+ 'performer',
21
+ ' file Queries/InvoiceSummaryPerformer.cs',
22
+ ]);
23
+ });
24
+
25
+ it('should find every reference', () => {
26
+ result.should.have.lengthOf(4);
27
+ });
28
+
29
+ it('should find them in document order', () => {
30
+ result.map((reference) => reference.path).should.deep.equal([
31
+ 'Validations/BeUnusedInvoiceNumber.cs',
32
+ 'Constraints/InvoiceStatusTransitionConstraint.cs',
33
+ 'Screens/InvoiceLineReport.tsx',
34
+ 'Queries/InvoiceSummaryPerformer.cs',
35
+ ]);
36
+ });
37
+
38
+ it('should report the line each one sits on', () => {
39
+ result.map((reference) => reference.line).should.deep.equal([1, 3, 5, 7]);
40
+ });
41
+ });
package/index.ts CHANGED
@@ -79,6 +79,13 @@ export { cdl } from './sub-languages/cdl';
79
79
  export { screenplayDarkThemeName } from './themes/screenplay-dark';
80
80
  export { screenplayLightThemeName } from './themes/screenplay-light';
81
81
  export { enclosingChain, fenceMap, firstWord, indentOf } from './document-context';
82
+ export {
83
+ fileReferenceKeyword,
84
+ fileReferenceOn,
85
+ fileReferences,
86
+ isAbsoluteFileReferencePath,
87
+ } from './file-references';
88
+ export type { FileReference } from './file-references';
82
89
  export { builtInTriggerNames, knownEventNames, knownTriggerNames, knownTypeNames, scanDocument } from './symbols';
83
90
  export type {
84
91
  CommandSymbol,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/screenplay-language",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Monaco language service for the Cratis Screenplay DSL — syntax highlighting, IntelliSense, hover and diagnostics for .play files",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,16 +33,20 @@
33
33
  "**/*.ts"
34
34
  ],
35
35
  "scripts": {
36
- "build": "tsc -p tsconfig.json",
36
+ "build": "tsc -p tsconfig.build.json",
37
37
  "compile": "tsc --noEmit -p tsconfig.json",
38
38
  "lint": "yarn g:lint",
39
- "lint:ci": "yarn g:lint:ci"
39
+ "lint:ci": "yarn g:lint:ci",
40
+ "test": "vitest run"
40
41
  },
41
42
  "peerDependencies": {
42
43
  "monaco-editor": ">=0.44.0"
43
44
  },
44
45
  "devDependencies": {
46
+ "@types/chai": "^5.2.3",
47
+ "chai": "^6.2.2",
45
48
  "monaco-editor": "^0.55.1",
46
- "typescript": "6.0.3"
49
+ "typescript": "6.0.3",
50
+ "vitest": "^4.1.11"
47
51
  }
48
52
  }
@@ -0,0 +1,5 @@
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
+ // Installs Chai's `.should` fluent interface, which every spec asserts through.
5
+ import 'chai/register-should';