Skip to content

Commit 67ca5b3

Browse files
authored
Local and Parameter Blocks (#1494)
* Updated grammar for Local and Parameters * Added new directive @INPUT_PARAMETER_BEGIN/END and @LOCALS_BEGIN/END * Update Linter to support the new grammar changes * Checking both the new and old Parameters @INPUT_PARAMS and @INPUT_PARAMS_BEGIN * Checking both the new and old Locals @Locals and @LOCALS_BEGIN * Update SeqJSON Exporter. * new LOCAL and INPUT_PARAMS blocks * SeqJson export of Local and Parameters are fully supported now * Update the unit test as well * Update SeqJSON Importer. * new LOCAL and INPUT_PARAMS blocks * Import full SeqJSON spec for Locals and Parameters * Update the unit test as well * Added the new directives to the autocomplete * Update Grammar test * Add support to fold Parameter and Local blocks * Updated the unit test * Fixed PR issues * Address PR issues from Bryan
1 parent 0848aa8 commit 67ca5b3

File tree

12 files changed

+575
-190
lines changed

12 files changed

+575
-190
lines changed

src/utilities/codemirror/custom-folder.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { EditorState } from '@codemirror/state';
22
import { describe, expect, test } from 'vitest';
3-
import { foldRequest, foldSteps } from './custom-folder';
3+
import { foldRequest, foldSteps, foldVariables } from './custom-folder';
44
import { parser } from './sequence.grammar';
55

66
const COMMANDS = [
@@ -23,6 +23,21 @@ const GROUND_ACTIVITIES = [
2323
'E-00:00:00.001 @GROUND_EVENT("event1")',
2424
];
2525

26+
const PARAMETERS = [
27+
`@INPUT_PARAMS_BEGIN
28+
VARIABLE INT "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."
29+
@INPUT_PARAMS_END`,
30+
`@INPUT_PARAMS_BEGIN
31+
@INPUT_PARAMS_END`,
32+
];
33+
34+
const LOCALS = [
35+
`@LOCALS_BEGIN
36+
VARIABLE INT
37+
TEST FLOAT
38+
@LOCALS_END`,
39+
];
40+
2641
const REQUEST_ACTIVITIES = [
2742
`G+00:00:00 "Name" @REQUEST_BEGIN("request.name")
2843
C SEQ_ECHO ""
@@ -345,3 +360,42 @@ describe('foldRequest', () => {
345360
});
346361
});
347362
});
363+
364+
describe('foldVariables', () => {
365+
test('Parameters should return the correct from and to', () => {
366+
PARAMETERS.forEach((parameter: string) => {
367+
const doc = parameter;
368+
const tree = parser.parse(doc);
369+
const state = EditorState.create({ doc });
370+
371+
const node = tree.topNode.getChild('ParameterDeclaration');
372+
if (!node) {
373+
throw new Error('node not found');
374+
} else {
375+
expect(node).not.toBeNull();
376+
expect(foldVariables(node, state)).toStrictEqual({
377+
from: '@INPUT_PARAMS_BEGIN'.length,
378+
to: doc.length,
379+
});
380+
}
381+
});
382+
});
383+
test('Locals should return the correct from and to', () => {
384+
LOCALS.forEach((local: string) => {
385+
const doc = local;
386+
const tree = parser.parse(doc);
387+
const state = EditorState.create({ doc });
388+
389+
const node = tree.topNode.getChild('LocalDeclaration');
390+
if (!node) {
391+
throw new Error('node not found');
392+
} else {
393+
expect(node).not.toBeNull();
394+
expect(foldVariables(node, state)).toStrictEqual({
395+
from: '@LOCALS_BEGIN'.length,
396+
to: doc.length,
397+
});
398+
}
399+
});
400+
});
401+
});

src/utilities/codemirror/custom-folder.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ export function customFoldInside(node: SyntaxNode, state: EditorState): { from:
2020
return foldMetadataOrModel(node, state, '@Metadata');
2121
case 'Models':
2222
return foldMetadataOrModel(node, state, '@Model');
23+
case 'ParameterDeclaration':
24+
case 'LocalDeclaration':
25+
return foldVariables(node, state);
2326
}
2427
return null;
2528
}
@@ -107,6 +110,39 @@ export function foldRequest(requestNode: SyntaxNode, state: EditorState): { from
107110
return { from, to: endRequest };
108111
}
109112

113+
/**
114+
* Calculate the fold range for a block of variables.
115+
* The fold range starts after the prefix of the block (e.g. "@INPUT_PARAMETERS_BEGIN" or "@LOCALS_BEGIN")
116+
* and ends of the block.
117+
* If the text ends with a line break, the end of the fold range will be adjusted to the start of the line break.
118+
*
119+
* @param containerNode - The node containing the variables (e.g. ParameterDeclaration or LocalDeclaration).
120+
* @param state - The EditorState object.
121+
* @returns A fold range object with "from" and "to" properties.
122+
* Returns null if any of the necessary nodes are not present.
123+
*/
124+
export function foldVariables(containerNode: SyntaxNode, state: EditorState): { from: number; to: number } | null {
125+
// Get all the Variable nodes in the container node
126+
const variablesNodes = containerNode.getChildren('Variable');
127+
128+
// Calculate the length of the directive (e.g. "@INPUT_PARAMETERS_BEGIN" or "@LOCALS_BEGIN")
129+
const directiveLength = state
130+
.sliceDoc(containerNode.from, containerNode.to - variablesNodes.length ? getFromAndTo([...variablesNodes]).from : 0)
131+
.split('\n')[0].length;
132+
133+
// Calculate the start of the fold range after the directive
134+
const from = containerNode.from + directiveLength;
135+
136+
// Calculate the end of the fold range after the last Variable node
137+
let endBlock = getFromAndTo([containerNode]).to;
138+
139+
// If the text ends with a line break, adjust the end of the fold range to the start of the line break
140+
const text = state.sliceDoc(containerNode.from + directiveLength, endBlock).trimEnd() + '\n';
141+
endBlock = from + text.lastIndexOf('\n');
142+
143+
return { from, to: endBlock };
144+
}
145+
110146
/**
111147
* Calculate the fold range for a Metadata or Model node.
112148
* The fold range starts after the node's prefix (e.g. "@Metadata" or "@Model") and ends after the node.

src/utilities/codemirror/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ export const SeqLanguage = LRLanguage.define({
1919
GroundBlock: customFoldInside,
2020
GroundEvent: customFoldInside,
2121
Load: customFoldInside,
22+
LocalDeclaration: customFoldInside,
2223
Metadata: customFoldInside,
2324
Models: customFoldInside,
25+
ParameterDeclaration: customFoldInside,
2426
Request: customFoldInside,
2527
}),
2628
styleTags({

src/utilities/codemirror/sequence.grammar

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ IdDeclaration {
2727
}
2828

2929
ParameterDeclaration {
30-
parameterDirective whiteSpace ((Enum whiteSpace?) | (Enum whiteSpace? Object whiteSpace?))+ newLine
30+
(parameterDirective whiteSpace Variable{((Enum whiteSpace?) | (Enum whiteSpace? Object whiteSpace?))}+ newLine) |
31+
(parameterStartDirective newLine (whiteSpace? Variable{((Enum whiteSpace?) | (Enum whiteSpace? Type { identifier } whiteSpace? ( (EnumName { identifier}) | (Range { String } (whiteSpace Values {String})? ) | (EnumName { identifier} whiteSpace (Range { String } (whiteSpace Values {String})?) )? whiteSpace?)) )} newLine)* parameterEndDirective newLine )
3132
}
3233

3334
LocalDeclaration {
34-
localsDirective (whiteSpace Enum)+ newLine
35+
(localsDirective whiteSpace Variable{((Enum whiteSpace?) | (Enum whiteSpace? Object whiteSpace?))}+ newLine) |
36+
(localsStartDirective newLine (whiteSpace? Variable{((Enum whiteSpace?) | (Enum whiteSpace? Type { identifier } whiteSpace? ( (EnumName { identifier}) | (Range { String } (whiteSpace Values {String})? ) | (EnumName { identifier} whiteSpace (Range { String } (whiteSpace Values {String})?) )? whiteSpace?)) )} newLine)* localsEndDirective newLine )
37+
3538
}
3639

3740
commandBlock {
@@ -208,7 +211,11 @@ Stem { !stemStart identifier }
208211
immediateDirective { "@IMMEDIATE" }
209212
hardwareDirective { "@HARDWARE" }
210213
localsDirective { "@LOCALS" }
214+
localsStartDirective { "@LOCALS_BEGIN" }
215+
localsEndDirective { "@LOCALS_END" }
211216
parameterDirective { "@INPUT_PARAMS" }
217+
parameterStartDirective { "@INPUT_PARAMS_BEGIN" }
218+
parameterEndDirective { "@INPUT_PARAMS_END" }
212219
activateDirective { "@ACTIVATE" }
213220
loadDirective { "@LOAD" }
214221
engineDirective { "@ENGINE" }
@@ -232,7 +239,11 @@ Stem { !stemStart identifier }
232239
modelDirective,
233240
immediateDirective,
234241
hardwareDirective,
242+
localsStartDirective
243+
localsEndDirective
235244
localsDirective,
245+
parameterStartDirective,
246+
parameterEndDirective,
236247
parameterDirective,
237248
activateDirective,
238249
loadDirective,

src/utilities/sequence-editor/from-seq-json.test.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ describe('from-seq-json.ts', async () => {
8181
};
8282
const sequence = await seqJsonToSequence(JSON.stringify(seqJson));
8383
const expectedSequence = `@ID "testSymbol"
84-
@LOCALS L00UINT L00INT L01INT
84+
@LOCALS_BEGIN
85+
L00UINT UINT
86+
L00INT INT
87+
L01INT INT
88+
@LOCALS_END
8589
8690
C PYRO_FIRE L00UINT 10 # line argument
8791
C DDM_BANANA L00INT L01INT
@@ -175,8 +179,20 @@ C FSW_CMD_3
175179
};
176180
const sequence = await seqJsonToSequence(JSON.stringify(seqJson));
177181
const expectedSequence = `@ID "testVariable"
178-
@INPUT_PARAMS L00INT { "type": "INT" } L01STR { "type": "STRING" } L02FLT { "type": "FLOAT" } L03UINT { "type": "UINT" } L01ENUM { "type": "ENUM" }
179-
@LOCALS L00INT L01STR L02FLT L03UINT L01ENUM
182+
@INPUT_PARAMS_BEGIN
183+
L00INT INT
184+
L01STR STRING
185+
L02FLT FLOAT
186+
L03UINT UINT
187+
L01ENUM ENUM
188+
@INPUT_PARAMS_END
189+
@LOCALS_BEGIN
190+
L00INT INT
191+
L01STR STRING
192+
L02FLT FLOAT
193+
L03UINT UINT
194+
L01ENUM ENUM
195+
@LOCALS_END
180196
`;
181197
expect(sequence).toEqual(expectedSequence);
182198
});

src/utilities/sequence-editor/from-seq-json.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -115,30 +115,24 @@ function seqJsonVariableToSequence(
115115
variables: [VariableDeclaration, ...VariableDeclaration[]],
116116
type: 'INPUT_PARAMS' | 'LOCALS',
117117
): string {
118-
let sequence = `@${type}`;
119-
120-
if (type === 'INPUT_PARAMS') {
121-
variables.forEach(variable => {
122-
sequence += ` ${variable.name} `;
123-
124-
if (Object.keys(variable).length > 1) {
125-
sequence += '{ ';
126-
127-
for (const key of Object.keys(variable)) {
128-
if (key !== 'name') {
129-
sequence += `"${key}": "${variable[key]}", `;
130-
}
131-
}
132-
133-
// Remove the trailing space and commma from the last property.
134-
sequence = `${sequence.substring(0, sequence.length - 2)} }`;
135-
}
136-
});
137-
} else {
138-
sequence += ` ${variables.map(variable => variable.name).join(' ')}`;
139-
}
118+
let sequence = `@${type}_BEGIN\n`;
119+
120+
sequence += variables
121+
.map(variable => {
122+
const name = variable.name;
123+
const type = variable.type ? ` ${variable.type}` : '';
124+
const enumName = variable.enum_name ? ` ${variable.enum_name}` : '';
125+
const allowableRanges = variable.allowable_ranges
126+
? ` "${variable.allowable_ranges.map(range => `${range.min}...${range.max}`).join(',')}"`
127+
: '';
128+
const allowableValues = variable.allowable_values
129+
? ` ${allowableRanges.length === 0 ? '"" ' : ''}"${variable.allowable_values.map(value => `${value}`).join(',')}"`
130+
: '';
131+
return `${name}${type}${enumName}${allowableRanges}${allowableValues}`;
132+
})
133+
.join('\n');
140134

141-
return sequence.trim() + '\n';
135+
return sequence.trim() + `\n@${type}_END\n`;
142136
}
143137

144138
function seqJsonDescriptionToSequence(description: Description): string {

src/utilities/sequence-editor/grammar.test.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ FSW_CMD 1 2
111111
FSW_CMD2`,
112112
`Sequence(
113113
LineComment,
114-
ParameterDeclaration(Enum,Object(Property(PropertyName(String),String))),
114+
ParameterDeclaration(Variable(Enum,Object(Property(PropertyName(String),String)))),
115115
LineComment,
116116
LineComment,
117-
LocalDeclaration(Enum,Enum),
117+
LocalDeclaration(Variable(Enum),Variable(Enum)),
118118
LineComment,
119119
Metadata(
120120
MetaEntry(Key(String),Value(String))
@@ -133,8 +133,8 @@ Command(Stem,Args)
133133
FSW_CMD 1 2
134134
FSW_CMD2`,
135135
`Sequence(
136-
ParameterDeclaration(Enum,Enum),
137-
LocalDeclaration(Enum,Enum),
136+
ParameterDeclaration(Variable(Enum),Variable(Enum)),
137+
LocalDeclaration(Variable(Enum),Variable(Enum)),
138138
Commands(
139139
Command(Stem,Args(Number,Number)),
140140
Command(Stem,Args)
@@ -148,8 +148,8 @@ Command(Stem,Args)
148148
FSW_CMD 1 2
149149
FSW_CMD2 "string val"`,
150150
`Sequence(
151-
ParameterDeclaration(Enum,Enum),
152-
LocalDeclaration(Enum,Enum),
151+
ParameterDeclaration(Variable(Enum),Variable(Enum)),
152+
LocalDeclaration(Variable(Enum),Variable(Enum)),
153153
Commands(
154154
Command(Stem,Args(Number,Number)),
155155
Command(Stem,Args(String))
@@ -301,9 +301,9 @@ CMD_2 "hello, it's me"
301301
`Sequence(
302302
IdDeclaration(String),
303303
LineComment,
304-
ParameterDeclaration(Enum),
304+
ParameterDeclaration(Variable(Enum)),
305305
LineComment,
306-
LocalDeclaration(Enum,Enum,Enum),
306+
LocalDeclaration(Variable(Enum),Variable(Enum),Variable(Enum)),
307307
Metadata(MetaEntry(Key(String),Value(String))),
308308
Commands(
309309
LoadAndGoDirective,
@@ -531,6 +531,38 @@ Command(TimeTag(TimeEpoch),Stem,Args),
531531
Command(TimeTag(TimeEpoch),Stem,Args)
532532
))`,
533533
],
534+
[
535+
'LOCALS BLOCKS',
536+
`@LOCALS_BEGIN
537+
VARIABLE INT
538+
VARIABLE INT "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."
539+
VARIABLE INT ENUM_NAME
540+
VARIABLE INT ENUM_NAME "MIN...MAX | ...MAX | MIN..."
541+
VARIABLE INT ENUM_NAME "" "VALUE_1, VALUE_2, ..."
542+
VARIABLE INT ENUM_NAME "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."
543+
@LOCALS_END`,
544+
`Sequence(LocalDeclaration(Variable(Enum,Type),Variable(Enum,Type,Range(String),Values(String)),Variable(Enum,Type,EnumName),Variable(Enum,Type,EnumName,Range(String)),Variable(Enum,Type,EnumName,Range(String),Values(String)),Variable(Enum,Type,EnumName,Range(String),Values(String))))`,
545+
],
546+
[
547+
'PARAMETER BLOCKS',
548+
`@INPUT_PARAMS_BEGIN
549+
VARIABLE INT
550+
VARIABLE INT "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."
551+
VARIABLE INT ENUM_NAME
552+
VARIABLE INT ENUM_NAME "MIN...MAX | ...MAX | MIN..."
553+
VARIABLE INT ENUM_NAME "" "VALUE_1, VALUE_2, ..."
554+
VARIABLE INT ENUM_NAME "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."
555+
@INPUT_PARAMS_END`,
556+
`Sequence(ParameterDeclaration(Variable(Enum,Type),Variable(Enum,Type,Range(String),Values(String)),Variable(Enum,Type,EnumName),Variable(Enum,Type,EnumName,Range(String)),Variable(Enum,Type,EnumName,Range(String),Values(String)),Variable(Enum,Type,EnumName,Range(String),Values(String))))`,
557+
],
558+
[
559+
'EMPTY LOCAL AND PARAM BLOCKS',
560+
`@INPUT_PARAMS_BEGIN
561+
@INPUT_PARAMS_END
562+
@LOCALS_BEGIN
563+
@LOCALS_END`,
564+
`Sequence(ParameterDeclaration,LocalDeclaration)`,
565+
],
534566
];
535567

536568
describe.each([

src/utilities/sequence-editor/sequence-completion.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,41 @@ export function sequenceCompletion(
8989
section: 'Directives',
9090
type: 'keyword',
9191
},
92+
{
93+
apply: `${cursor.isAtSymbolBefore ? '' : '@'}INPUT_PARAMS_BEGIN\nVARIABLE INT "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."\n@INPUT_PARAMS_END`,
94+
info: 'Block of Input Parameters',
95+
label: '@INPUT_PARAMS_BEGIN',
96+
section: 'Directives',
97+
type: 'keyword',
98+
},
99+
{
100+
apply: `${cursor.isAtSymbolBefore ? '' : '@'}INPUT_PARAMS_END\n`,
101+
info: 'END block of Input Parameters',
102+
label: '@INPUT_PARAMS_END',
103+
section: 'Directives',
104+
type: 'keyword',
105+
},
92106
{
93107
apply: `${cursor.isAtSymbolBefore ? '' : '@'}LOCALS VALUE`,
94108
info: 'List of Local Variables',
95109
label: '@LOCALS',
96110
section: 'Directives',
97111
type: 'keyword',
98112
},
113+
{
114+
apply: `${cursor.isAtSymbolBefore ? '' : '@'}LOCALS_BEGIN\nVARIABLE INT "MIN...MAX | ...MAX | MIN..." "VALUE_1, VALUE_2, ..."\n@LOCALS_END`,
115+
info: 'Block of Local Variables',
116+
label: '@LOCALS_BEGIN',
117+
section: 'Directives',
118+
type: 'keyword',
119+
},
120+
{
121+
apply: `${cursor.isAtSymbolBefore ? '' : '@'}LOCALS_END\n`,
122+
info: 'END block of Local Variables',
123+
label: '@LOCALS_END',
124+
section: 'Directives',
125+
type: 'keyword',
126+
},
99127
{
100128
apply: `${cursor.isAtSymbolBefore ? '' : '@'}HARDWARE`,
101129
info: 'A HARDWARE Directive',

0 commit comments

Comments
 (0)