Skip to content

Commit b0a4682

Browse files
authored
Show appropriate warning on block that calls a method on a mechanism's private component (#239)
* Update mrc_call_python_function mrcOnLoad method to check whether a mechanism component is now a private component. Removed field mrcComponentNames. It only needs to be a local variable in mrcOnLoad. Added method Editor.getPrivateComponentsFromMechanism. * Localized strings in mrc_call_python_function.ts.
1 parent 14f9b65 commit b0a4682

File tree

6 files changed

+212
-48
lines changed

6 files changed

+212
-48
lines changed

src/blocks/mrc_call_python_function.ts

Lines changed: 97 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
8585
mrcComponentClassName: string,
8686
mrcOriginalComponentName: string,
8787
mrcMechanismClassName: string,
88-
mrcComponentNames: string[],
8988
mrcMapComponentNameToId: {[componentName: string]: string},
9089
}
9190
type CallPythonFunctionMixinType = typeof CALL_PYTHON_FUNCTION;
@@ -167,65 +166,88 @@ const CALL_PYTHON_FUNCTION = {
167166
switch (this.mrcFunctionKind) {
168167
case FunctionKind.BUILT_IN: {
169168
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
170-
tooltip = 'Calls the builtin function ' + functionName + '.';
169+
tooltip = Blockly.Msg.CALL_BUILTIN_FUNCTION_TOOLTIP;
170+
tooltip = tooltip.replace('{{functionName}}', functionName);
171171
break;
172172
}
173173
case FunctionKind.MODULE: {
174174
const moduleName = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
175175
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
176-
tooltip = 'Calls the module function ' + moduleName + '.' + functionName + '.';
176+
tooltip = Blockly.Msg.CALL_MODULE_FUNCTION_TOOLTIP;
177+
tooltip = tooltip
178+
.replace('{{moduleName}}', moduleName)
179+
.replace('{{functionName}}', functionName);
177180
break;
178181
}
179182
case FunctionKind.STATIC: {
180183
const className = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
181184
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
182-
tooltip = 'Calls the static method ' + className + '.' + functionName + '.';
185+
tooltip = Blockly.Msg.CALL_STATIC_METHOD_TOOLTIP;
186+
tooltip = tooltip
187+
.replace('{{className}}', className)
188+
.replace('{{functionName}}', functionName);
183189
break;
184190
}
185191
case FunctionKind.CONSTRUCTOR: {
186192
const className = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
187-
tooltip = 'Constructs an instance of the class ' + className + '.';
193+
tooltip = Blockly.Msg.CALL_CONSTRUCTOR_TOOLTIP;
194+
tooltip = tooltip.replace('{{className}}', className);
188195
break;
189196
}
190197
case FunctionKind.INSTANCE: {
191198
const className = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
192199
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
193-
tooltip = 'Calls the instance method ' + className + '.' + functionName + '.';
200+
tooltip = Blockly.Msg.CALL_INSTANCE_METHOD_TOOLTIP;
201+
tooltip = tooltip
202+
.replace('{{className}}', className)
203+
.replace('{{functionName}}', functionName);
194204
break;
195205
}
196206
case FunctionKind.INSTANCE_WITHIN: {
197207
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
198-
tooltip = 'Calls the instance method ' + functionName + '.';
208+
tooltip = Blockly.Msg.CALL_INSTANCE_METHOD_WITHIN_TOOLTIP;
209+
tooltip = tooltip.replace('{{functionName}}', functionName);
199210
break;
200211
}
201212
case FunctionKind.EVENT: {
202213
const eventName = this.getFieldValue(FIELD_EVENT_NAME);
203-
tooltip = 'Fires the event ' + eventName + '.';
214+
tooltip = Blockly.Msg.CALL_INSTANCE_METHOD_WITHIN_TOOLTIP;
215+
tooltip = tooltip.replace('{{eventName}}', eventName);
204216
break;
205217
}
206218
case FunctionKind.INSTANCE_COMPONENT: {
207219
const className = this.mrcComponentClassName;
208220
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
209221
if (this.mrcMechanismId) {
210-
tooltip = 'Calls the instance method ' + className + '.' + functionName +
211-
' on the component named ' + this.getFieldValue(FIELD_COMPONENT_NAME) +
212-
' in the mechanism named ' + this.getFieldValue(FIELD_MECHANISM_NAME) + '.';
222+
tooltip = Blockly.Msg.CALL_MECHANISM_COMPONENT_INSTANCE_METHOD;
223+
tooltip = tooltip
224+
.replace('{{className}}', className)
225+
.replace('{{functionName}}', functionName)
226+
.replace('{{componentName}}', this.getFieldValue(FIELD_COMPONENT_NAME))
227+
.replace('{{mechanismName}}', this.getFieldValue(FIELD_MECHANISM_NAME));
213228
} else {
214-
tooltip = 'Calls the instance method ' + className + '.' + functionName +
215-
' on the component named ' + this.getFieldValue(FIELD_COMPONENT_NAME) + '.';
229+
tooltip = Blockly.Msg.CALL_COMPONENT_INSTANCE_METHOD;
230+
tooltip = tooltip
231+
.replace('{{className}}', className)
232+
.replace('{{functionName}}', functionName)
233+
.replace('{{componentName}}', this.getFieldValue(FIELD_COMPONENT_NAME));
216234
}
217235
break;
218236
}
219237
case FunctionKind.INSTANCE_ROBOT: {
220238
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
221-
tooltip = 'Calls the robot method ' + functionName + '.';
239+
tooltip = Blockly.Msg.CALL_INSTANCE_METHOD_WITHIN_TOOLTIP;
240+
tooltip = tooltip.replace('{{functionName}}', functionName);
222241
break;
223242
}
224243
case FunctionKind.INSTANCE_MECHANISM: {
225244
const className = this.mrcMechanismClassName;
226245
const functionName = this.getFieldValue(FIELD_FUNCTION_NAME);
227-
tooltip = 'Calls the instance method ' + className + '.' + functionName +
228-
' on the mechanism named ' + this.getFieldValue(FIELD_MECHANISM_NAME) + '.';
246+
tooltip = Blockly.Msg.CALL_MECHANISM_INSTANCE_METHOD;
247+
tooltip = tooltip
248+
.replace('{{className}}', className)
249+
.replace('{{functionName}}', functionName)
250+
.replace('{{mechanismName}}', this.getFieldValue(FIELD_MECHANISM_NAME));
229251
break;
230252
}
231253
default:
@@ -318,8 +340,7 @@ const CALL_PYTHON_FUNCTION = {
318340
this.mrcMechanismId = extraState.mechanismId ? extraState.mechanismId : '';
319341
this.mrcComponentClassName = extraState.componentClassName ? extraState.componentClassName : '';
320342
this.mrcMechanismClassName = extraState.mechanismClassName ? extraState.mechanismClassName : '';
321-
// Initialize mrcComponentNames and mrcMapComponentNameToId here. They will be filled during mrcValidate.
322-
this.mrcComponentNames = [];
343+
// Initialize mrcMapComponentNameToId here. It will be filled during mrcValidate.
323344
this.mrcMapComponentNameToId = {};
324345
this.updateBlock_();
325346
},
@@ -349,31 +370,31 @@ const CALL_PYTHON_FUNCTION = {
349370
switch (this.mrcFunctionKind) {
350371
case FunctionKind.BUILT_IN:
351372
this.appendDummyInput(INPUT_TITLE)
352-
.appendField('call')
373+
.appendField(Blockly.Msg.CALL)
353374
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
354375
break;
355376
case FunctionKind.MODULE:
356377
this.appendDummyInput(INPUT_TITLE)
357-
.appendField('call')
378+
.appendField(Blockly.Msg.CALL)
358379
.appendField(createFieldNonEditableText(''), FIELD_MODULE_OR_CLASS_NAME)
359380
.appendField('.')
360381
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
361382
break;
362383
case FunctionKind.STATIC:
363384
this.appendDummyInput(INPUT_TITLE)
364-
.appendField('call')
385+
.appendField(Blockly.Msg.CALL)
365386
.appendField(createFieldNonEditableText(''), FIELD_MODULE_OR_CLASS_NAME)
366387
.appendField('.')
367388
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
368389
break;
369390
case FunctionKind.CONSTRUCTOR:
370391
this.appendDummyInput(INPUT_TITLE)
371-
.appendField('create')
392+
.appendField(Blockly.Msg.CREATE)
372393
.appendField(createFieldNonEditableText(''), FIELD_MODULE_OR_CLASS_NAME);
373394
break;
374395
case FunctionKind.INSTANCE:
375396
this.appendDummyInput(INPUT_TITLE)
376-
.appendField('call')
397+
.appendField(Blockly.Msg.CALL)
377398
.appendField(createFieldNonEditableText(''), FIELD_MODULE_OR_CLASS_NAME)
378399
.appendField('.')
379400
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
@@ -382,7 +403,7 @@ const CALL_PYTHON_FUNCTION = {
382403
const input = this.getInput(INPUT_TITLE);
383404
if (!input) {
384405
this.appendDummyInput(INPUT_TITLE)
385-
.appendField('call')
406+
.appendField(Blockly.Msg.CALL)
386407
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
387408
}
388409
break;
@@ -391,14 +412,14 @@ const CALL_PYTHON_FUNCTION = {
391412
const input = this.getInput(INPUT_TITLE);
392413
if (!input) {
393414
this.appendDummyInput(INPUT_TITLE)
394-
.appendField('fire')
415+
.appendField(Blockly.Msg.FIRE)
395416
.appendField(createFieldNonEditableText(''), FIELD_EVENT_NAME);
396417
}
397418
break;
398419
}
399420
case FunctionKind.INSTANCE_COMPONENT: {
400421
const titleInput = this.appendDummyInput(INPUT_TITLE)
401-
.appendField('call');
422+
.appendField(Blockly.Msg.CALL);
402423
if (this.mrcMechanismId) {
403424
titleInput
404425
.appendField(createFieldNonEditableText(''), FIELD_MECHANISM_NAME)
@@ -414,15 +435,15 @@ const CALL_PYTHON_FUNCTION = {
414435
}
415436
case FunctionKind.INSTANCE_ROBOT: {
416437
this.appendDummyInput(INPUT_TITLE)
417-
.appendField('call')
418-
.appendField(createFieldNonEditableText('robot'))
438+
.appendField(Blockly.Msg.CALL)
439+
.appendField(createFieldNonEditableText(Blockly.Msg.ROBOT))
419440
.appendField('.')
420441
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
421442
break;
422443
}
423444
case FunctionKind.INSTANCE_MECHANISM: {
424445
this.appendDummyInput(INPUT_TITLE)
425-
.appendField('call')
446+
.appendField(Blockly.Msg.CALL)
426447
.appendField(createFieldNonEditableText(''), FIELD_MECHANISM_NAME)
427448
.appendField('.')
428449
.appendField(createFieldNonEditableText(''), FIELD_FUNCTION_NAME);
@@ -505,7 +526,7 @@ const CALL_PYTHON_FUNCTION = {
505526
methodOrEvent: storageModuleContent.Method | storageModuleContent.Event
506527
): void {
507528
// mutateMethodCaller is called when the method or event definition block in the same module is modified.
508-
if (this.mrcFunctionKind == FunctionKind.EVENT) {
529+
if (this.mrcFunctionKind === FunctionKind.EVENT) {
509530
const event = methodOrEvent as storageModuleContent.Event;
510531
this.mrcArgs = [];
511532
event.args.forEach((arg) => {
@@ -514,7 +535,7 @@ const CALL_PYTHON_FUNCTION = {
514535
type: arg.type,
515536
});
516537
});
517-
} else if (this.mrcFunctionKind == FunctionKind.INSTANCE_WITHIN) {
538+
} else if (this.mrcFunctionKind === FunctionKind.INSTANCE_WITHIN) {
518539
const method = methodOrEvent as storageModuleContent.Method;
519540
this.mrcReturnType = method.returnType;
520541
this.mrcArgs = [];
@@ -591,12 +612,14 @@ const CALL_PYTHON_FUNCTION = {
591612
// If the component belongs to a mechanism, also check whether the mechanism
592613
// still exists and whether it has been changed.
593614
if (this.mrcFunctionKind === FunctionKind.INSTANCE_COMPONENT) {
615+
const componentNames: string[] = [];
616+
this.mrcMapComponentNameToId = {}
594617
this.getComponents().forEach(component => {
595-
this.mrcComponentNames.push(component.name);
618+
componentNames.push(component.name);
596619
this.mrcMapComponentNameToId[component.name] = component.componentId;
597620
});
598621
let foundComponent = false;
599-
for (const componentName of this.mrcComponentNames) {
622+
for (const componentName of componentNames) {
600623
const componentId = this.mrcMapComponentNameToId[componentName];
601624
if (componentId === this.mrcComponentId) {
602625
foundComponent = true;
@@ -615,12 +638,12 @@ const CALL_PYTHON_FUNCTION = {
615638
break;
616639
}
617640
}
618-
if (indexOfComponentNameField == -1) {
641+
if (indexOfComponentNameField === -1) {
619642
throw new Error('Could not find the component name field');
620643
}
621644
titleInput.removeField(FIELD_COMPONENT_NAME);
622645
titleInput.insertFieldAt(indexOfComponentNameField,
623-
createFieldDropdown(this.mrcComponentNames), FIELD_COMPONENT_NAME);
646+
createFieldDropdown(componentNames), FIELD_COMPONENT_NAME);
624647
// TODO(lizlooney): If the current module is the robot or a mechanism, we need to update the
625648
// items in the dropdown if the user adds or removes a component.
626649

@@ -631,7 +654,39 @@ const CALL_PYTHON_FUNCTION = {
631654
}
632655
}
633656
if (!foundComponent) {
634-
warnings.push('This block calls a method on a component that no longer exists.');
657+
if (this.mrcMechanismId) {
658+
// Check whether the the component still exists, but is a private component in the mechanism.
659+
for (const mechanismInRobot of editor.getMechanismsFromRobot()) {
660+
if (mechanismInRobot.mechanismId === this.mrcMechanismId) {
661+
for (const mechanism of editor.getMechanisms()) {
662+
if (mechanism.moduleId === mechanismInRobot.moduleId) {
663+
for (const privateComponent of editor.getPrivateComponentsFromMechanism(mechanism)) {
664+
if (privateComponent.className === this.mrcComponentClassName &&
665+
privateComponent.componentId === this.mrcComponentId) {
666+
foundComponent = true;
667+
let warning = Blockly.Msg.WARNING_CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT;
668+
warning = warning.replace('{{mechanismClassName}}', mechanism.className);
669+
warnings.push(warning);
670+
break
671+
}
672+
}
673+
break;
674+
}
675+
if (foundComponent) {
676+
break;
677+
}
678+
}
679+
break;
680+
}
681+
if (foundComponent) {
682+
break;
683+
}
684+
}
685+
}
686+
}
687+
688+
if (!foundComponent) {
689+
warnings.push(Blockly.Msg.WARNING_CALL_COMPONENT_INSTANCE_METHOD_MISSING_COMPONENT);
635690
}
636691

637692
if (this.mrcMechanismId) {
@@ -649,9 +704,7 @@ const CALL_PYTHON_FUNCTION = {
649704
}
650705
}
651706
if (!foundMechanism) {
652-
warnings.push(
653-
'This block calls a method on a component that belongs to a mechanism that no ' +
654-
'longer exists.');
707+
warnings.push(Blockly.Msg.WARNING_CALL_MECHANISM_COMPONENT_INSTANCE_METHOD_MISSING_MECHANISM);
655708
}
656709
}
657710

@@ -665,7 +718,7 @@ const CALL_PYTHON_FUNCTION = {
665718
// visible warning on it.
666719
if (this.mrcFunctionKind === FunctionKind.INSTANCE_ROBOT) {
667720
if (editor.getModuleType() === storageModule.ModuleType.MECHANISM) {
668-
warnings.push('This block is not allowed to be used inside a mechanism.');
721+
warnings.push(Blockly.Msg.WARNING_CALL_ROBOT_INSTANCE_METHOD_INSIDE_MECHANISM);
669722
} else {
670723
let foundRobotMethod = false;
671724
const robotMethods = editor.getMethodsFromRobot();
@@ -695,7 +748,7 @@ const CALL_PYTHON_FUNCTION = {
695748
}
696749
}
697750
if (!foundRobotMethod) {
698-
warnings.push('This block calls a method that no longer exists.');
751+
warnings.push(Blockly.Msg.WARNING_CALL_ROBOT_INSTANCE_METHOD_MISSING_METHOD);
699752
}
700753
}
701754
}
@@ -711,7 +764,7 @@ const CALL_PYTHON_FUNCTION = {
711764
// visible warning on it.
712765
if (this.mrcFunctionKind === FunctionKind.INSTANCE_MECHANISM) {
713766
if (editor.getModuleType() === storageModule.ModuleType.MECHANISM) {
714-
warnings.push('This block is not allowed to be used inside a mechanism.');
767+
warnings.push(Blockly.Msg.WARNING_CALL_MECHANISM_INSTANCE_METHOD_INSIDE_MECHANISM);
715768
} else {
716769
let foundMechanism = false;
717770
const mechanismsInRobot = editor.getMechanismsFromRobot();
@@ -753,15 +806,15 @@ const CALL_PYTHON_FUNCTION = {
753806
}
754807
}
755808
if (!foundMechanismMethod) {
756-
warnings.push('This block calls a method that no longer exists.');
809+
warnings.push(Blockly.Msg.WARNING_CALL_MECHANISM_INSTANCE_METHOD_MISSING_METHOD);
757810
}
758811

759812
// Since we found the mechanism, we can break out of the loop.
760813
break;
761814
}
762815
}
763816
if (!foundMechanism) {
764-
warnings.push('This block calls a method in a mechanism that no longer exists.');
817+
warnings.push(Blockly.Msg.WARNING_CALL_MECHANISM_INSTANCE_METHOD_MISSING_MECHANISM);
765818
}
766819
}
767820
}

src/blocks/tokens.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
6161
OPMODE_ENABLED_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_ENABLED'),
6262
OPMODE_NAME_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_NAME'),
6363
OPMODE_GROUP_TOOLTIP: t('BLOCKLY.TOOLTIP.OPMODE_GROUP'),
64+
CALL: t('BLOCKLY.CALL'),
65+
ROBOT_LOWER_CASE: t('BLOCKLY.ROBOT_LOWER_CASE'),
66+
CREATE: t('BLOCKLY.CREATE'),
67+
FIRE: t('BLOCKLY.FIRE'),
68+
WARNING_CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT: t('BLOCKLY.WARNING.CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT'),
69+
WARNING_CALL_COMPONENT_INSTANCE_METHOD_MISSING_COMPONENT: t('BLOCKLY.WARNING.CALL_COMPONENT_INSTANCE_METHOD_MISSING_COMPONENT'),
70+
WARNING_CALL_MECHANISM_COMPONENT_INSTANCE_METHOD_MISSING_MECHANISM: t('BLOCKLY.WARNING.CALL_MECHANISM_COMPONENT_INSTANCE_METHOD_MISSING_MECHANISM'),
71+
WARNING_CALL_ROBOT_INSTANCE_METHOD_INSIDE_MECHANISM: t('BLOCKLY.WARNING.CALL_ROBOT_INSTANCE_METHOD_INSIDE_MECHANISM'),
72+
WARNING_CALL_ROBOT_INSTANCE_METHOD_MISSING_METHOD: t('BLOCKLY.WARNING.CALL_ROBOT_INSTANCE_METHOD_MISSING_METHOD'),
73+
WARNING_CALL_MECHANISM_INSTANCE_METHOD_INSIDE_MECHANISM: t('BLOCKLY.WARNING.CALL_MECHANISM_INSTANCE_METHOD_INSIDE_MECHANISM'),
74+
WARNING_CALL_MECHANISM_INSTANCE_METHOD_MISSING_METHOD: t('BLOCKLY.WARNING.CALL_MECHANISM_INSTANCE_METHOD_MISSING_METHOD'),
75+
WARNING_CALL_MECHANISM_INSTANCE_METHOD_MISSING_MECHANISM: t('BLOCKLY.WARNING.CALL_MECHANISM_INSTANCE_METHOD_MISSING_MECHANISM'),
76+
CALL_BUILTIN_FUNCTION_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_BUILTIN_FUNCTION'),
77+
CALL_MODULE_FUNCTION_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_MODULE_FUNCTION'),
78+
CALL_STATIC_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_STATIC_METHOD'),
79+
CALL_CONSTRUCTOR_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_CONSTRUCTOR'),
80+
CALL_INSTANCE_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_INSTANCE_METHOD'),
81+
CALL_INSTANCE_METHOD_WITHIN_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_INSTANCE_METHOD_WITHIN'),
82+
FIRE_EVENT_TOOLTIP: t('BLOCKLY.TOOLTIP.FIRE_EVENT'),
83+
CALL_MECHANISM_COMPONENT_INSTANCE_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_MECHANISM_COMPONENT_INSTANCE_METHOD'),
84+
CALL_COMPONENT_INSTANCE_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_COMPONENT_INSTANCE_METHOD'),
85+
CALL_ROBOT_INSTANCE_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_ROBOT_INSTANCE_METHOD'),
86+
CALL_MECHANISM_INSTANCE_METHOD_TOOLTIP: t('BLOCKLY.TOOLTIP.CALL_MECHANISM_INSTANCE_METHOD'),
6487
MRC_CATEGORY_HARDWARE: t('BLOCKLY.CATEGORY.HARDWARE'),
6588
MRC_CATEGORY_ROBOT: t('BLOCKLY.CATEGORY.ROBOT'),
6689
MRC_CATEGORY_COMPONENTS: t('BLOCKLY.CATEGORY.COMPONENTS'),

0 commit comments

Comments
 (0)