Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions frontend/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Order } from 'blockly/python';

import { MRC_STYLE_FUNCTIONS } from '../themes/styles';
import { createFieldNonEditableText } from '../fields/FieldNonEditableText'
import { createParameterFieldFlydown } from '../fields/field_flydown';
import { createParameterFieldFlydown, createReturnFieldFlydown } from '../fields/field_flydown';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import * as storageModuleContent from '../storage/module_content';
import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function'
Expand Down Expand Up @@ -38,7 +38,7 @@ const UNTYPED_RETURN_VALUE = '';
const INPUT_TITLE = 'TITLE';
const FIELD_METHOD_NAME = 'NAME';
const FIELD_PARAM_PREFIX = 'PARAM_';
const INPUT_STACK = 'STACK';
export const INPUT_STACK = 'STACK';
export const INPUT_RETURN = 'RETURN';

export interface Parameter {
Expand Down Expand Up @@ -266,7 +266,7 @@ const CLASS_METHOD_DEF = {
if (this.mrcReturnType !== undefined && this.mrcReturnType !== NO_RETURN_VALUE) {
this.appendValueInput(INPUT_RETURN)
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
.appendField(createReturnFieldFlydown());
// Move the return input to be after the statement input
this.moveInputBefore(INPUT_STACK, INPUT_RETURN);
}
Expand Down
109 changes: 109 additions & 0 deletions frontend/blocks/mrc_return.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) FIRST and other WPILib contributors.

/**
* @fileoverview This is a block that returns a value from a method.
* @author alan@porpoiseful.com (Alan Smith)
*/
import * as Blockly from 'blockly';
import { Order } from 'blockly/python';

import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { MRC_STYLE_FUNCTIONS } from '../themes/styles';
import { BLOCK_NAME as MRC_CLASS_METHOD_DEF, INPUT_RETURN, INPUT_STACK } from './mrc_class_method_def';
Comment thread
alan412 marked this conversation as resolved.
Resolved
import { findConnectedBlocksOfType } from './utils/find_connected_blocks';
import { getValueType } from './utils/variable_types';

export const BLOCK_NAME = 'mrc_return';

const INPUT_VALUE = 'VALUE';

const WARNING_ID_NOT_IN_METHOD = 'MRC_RETURN_NOT_IN_METHOD';
const WARNING_ID_DIFFERENT_TYPES = 'MRC_RETURN_DIFFERENT_TYPES';

const RETURN_BLOCK = {
/**
* Block initialization.
*/
init: function (this: Blockly.Block): void {
this.setStyle(MRC_STYLE_FUNCTIONS);
this.appendValueInput(INPUT_VALUE)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.setPreviousStatement(true, null);
this.setNextStatement(false);
},
};

export const setup = function () {
Blockly.Blocks[BLOCK_NAME] = RETURN_BLOCK;
};

export const pythonFromBlock = function (
block: Blockly.Block,
generator: ExtendedPythonGenerator,
) {
const value = generator.valueToCode(block, INPUT_VALUE, Order.NONE) || 'None';
return 'return ' + value + '\n';
};

/**
* Returns true if the given block is a method definition that returns a value.
*/
function isMethodWithReturn(block: Blockly.Block): boolean {
return block.type === MRC_CLASS_METHOD_DEF && block.getInput(INPUT_RETURN) !== null;
}

/**
* Puts a warning on each return block that isn't in a method that returns a value. For each
* method, if the values returned (by its return blocks and by the return input of the method
* block) have different types, puts a warning on each of the blocks that returns a value with a
* type. Values that don't have a type are ignored, since they might be of any type.
*/
export function updateReturnWarnings(workspace: Blockly.Workspace): void {
if (workspace.isFlyout) {
return;
}
const differentTypesWarnings = new Map<Blockly.Block, string>();
const methodBlocks = workspace.getBlocksByType(MRC_CLASS_METHOD_DEF, false);
for (const methodBlock of methodBlocks) {
// Each block that returns a value, with the input that holds the value.
const returningBlocks: [Blockly.Block, string][] = [];
if (isMethodWithReturn(methodBlock)) {
returningBlocks.push([methodBlock, INPUT_RETURN]);
}
const stackBlock = methodBlock.getInputTargetBlock(INPUT_STACK);
if (stackBlock) {
findConnectedBlocksOfType(stackBlock, BLOCK_NAME).forEach(returnBlock => {
returningBlocks.push([returnBlock, INPUT_VALUE]);
});
}

const typedBlocks: Blockly.Block[] = [];
const types = new Set<string>();
returningBlocks.forEach(([block, inputName]) => {
const valueBlock = block.getInputTargetBlock(inputName);
const type = valueBlock ? getValueType(valueBlock) : '';
if (type) {
typedBlocks.push(block);
types.add(type);
}
});
if (types.size > 1) {
const warning = Blockly.Msg.WARNING_RETURN_DIFFERENT_TYPES
.replace('{{types}}', [...types].join(', '));
typedBlocks.forEach(block => differentTypesWarnings.set(block, warning));
}
}

for (const methodBlock of methodBlocks) {
methodBlock.setWarningText(
differentTypesWarnings.get(methodBlock) ?? null, WARNING_ID_DIFFERENT_TYPES);
}
for (const block of workspace.getBlocksByType(BLOCK_NAME, false)) {
block.setWarningText(
isMethodWithReturn(block.getRootBlock())
? null : Blockly.Msg.RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN,
WARNING_ID_NOT_IN_METHOD);
block.setWarningText(
differentTypesWarnings.get(block) ?? null, WARNING_ID_DIFFERENT_TYPES);
}
}
2 changes: 2 additions & 0 deletions frontend/blocks/setup_custom_blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as OpModeDetails from './mrc_opmode_details';
import * as OutputContainer from './mrc_output_container';
import * as ParamContainer from './mrc_param_container'
import * as Port from './mrc_port';
import * as Return from './mrc_return';
import * as SetPythonVariable from './mrc_set_python_variable';
import * as Steps from './mrc_steps';
import * as When from './mrc_when';
Expand Down Expand Up @@ -54,6 +55,7 @@ const customBlocks = [
OutputContainer,
ParamContainer,
Port,
Return,
SetPythonVariable,
Steps,
When,
Expand Down
3 changes: 3 additions & 0 deletions frontend/blocks/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
t('BLOCKLY.PARAMETER_DOES_NOT_EXIST_IN_EVENT_HANDLER'),
NO_PARAMETER_SELECTED:
t('BLOCKLY.NO_PARAMETER_SELECTED'),
RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN:
t('BLOCKLY.RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN'),
CANNOT_MOVE_BLOCKS_TO_OTHER_TAB:
t('BLOCKLY.CANNOT_MOVE_BLOCKS_TO_OTHER_TAB'),
CANNOT_MOVE_METHOD_OVERRIDE_TO_OTHER_TAB:
Expand Down Expand Up @@ -196,6 +198,7 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
WARNING_CALL_INSTANCE_METHOD_MISSING_CLASS: t('BLOCKLY.WARNING.CALL_INSTANCE_METHOD_MISSING_CLASS'),
WARNING_CALL_INSTANCE_VARIABLE_WRONG_TYPE: t('BLOCKLY.WARNING.CALL_INSTANCE_VARIABLE_WRONG_TYPE'),
WARNING_SET_VARIABLE_DIFFERENT_TYPE: t('BLOCKLY.WARNING.SET_VARIABLE_DIFFERENT_TYPE'),
WARNING_RETURN_DIFFERENT_TYPES: t('BLOCKLY.WARNING.RETURN_DIFFERENT_TYPES'),
WARNING_CALL_INSTANCE_METHOD_MISSING_METHOD: t('BLOCKLY.WARNING.CALL_INSTANCE_METHOD_MISSING_METHOD'),
WARNING_CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD: t('BLOCKLY.WARNING.CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD'),
WARNING_CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT: t('BLOCKLY.WARNING.CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT'),
Expand Down
2 changes: 1 addition & 1 deletion frontend/blocks/utils/variable_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export function getDifferentTypeWarning(block: Blockly.Block): string | null {
}

/** Returns the type of the value of the given block, or '' if it doesn't have a type. */
function getValueType(block: Blockly.Block): string {
export function getValueType(block: Blockly.Block): string {
if (block.type === BLOCK_TYPE_VARIABLES_GET) {
const field = block.getField(FIELD_VAR) as Blockly.FieldVariable | null;
return field?.getVariable()?.getType() ?? '';
Expand Down
4 changes: 4 additions & 0 deletions frontend/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as blockSteps from '../blocks/mrc_steps';
import * as mechanismComponentHolder from '../blocks/mrc_mechanism_component_holder';
import * as portConflicts from '../blocks/utils/port_conflicts';
import { canChangeVariableTypes, updateVariableTypes } from '../blocks/utils/variable_types';
import { updateReturnWarnings } from '../blocks/mrc_return';
import * as workspaces from '../blocks/utils/workspaces';
//import { testAllBlocksInToolbox } from '../toolbox/toolbox_tests';
import { applyExpandedCategories, getToolboxJSON } from '../toolbox/toolbox';
Expand Down Expand Up @@ -134,6 +135,7 @@ export class Editor {

// Variables in projects saved before variables had types get their types now.
updateVariableTypes(this.blocklyWorkspace);
updateReturnWarnings(this.blocklyWorkspace);
}

private onChangeAfterLoading(event: Blockly.Events.Abstract) {
Expand All @@ -159,6 +161,8 @@ export class Editor {
// Variables get their types from the values assigned to them.
if (canChangeVariableTypes(event)) {
updateVariableTypes(this.blocklyWorkspace);
// Return values can have different types after blocks move or variable types change.
updateReturnWarnings(this.blocklyWorkspace);
}

if (event instanceof Blockly.Events.VarTypeChange) {
Expand Down
18 changes: 17 additions & 1 deletion frontend/fields/field_flydown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import * as Blockly from 'blockly';
import {BLOCK_NAME as JUMP_TO_STEP} from '../blocks/mrc_jump_to_step';
import {BLOCK_NAME as GET_PARAMETER} from '../blocks/mrc_get_parameter';
import {BLOCK_NAME as RETURN} from '../blocks/mrc_return';
import {matchBorderToText} from './FieldNonEditableText';

const NON_EDITABLE_CSS_CLASS_NAME = 'mrcNonEditableFlydownField';
Expand Down Expand Up @@ -406,6 +407,17 @@ function createParameterBlock(paramName: string): Blockly.utils.toolbox.FlyoutDe
};
}

function createReturnBlock(_text: string): Blockly.utils.toolbox.FlyoutDefinition {
return {
contents: [
{
kind: 'block',
type: RETURN,
},
]
};
}

function createJumpToStepBlock(stepName: string): Blockly.utils.toolbox.FlyoutDefinition {
return {
contents: [
Expand Down Expand Up @@ -434,4 +446,8 @@ export function createParameterFieldFlydown(paramName: string, isEditable: boole

export function createStepFieldFlydown(stepName: string, isEditable: boolean): Blockly.Field {
return new FieldFlydown(stepName, isEditable, createJumpToStepBlock);
}
}

export function createReturnFieldFlydown(): Blockly.Field {
return new FieldFlydown(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN, false, createReturnBlock);
}
2 changes: 2 additions & 0 deletions frontend/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@
"PARAMETER_DOES_NOT_EXIST_IN_EVENT_HANDLER": "Parameter \"%1\" does not exist in this event handler.",
"NO_PARAMETER_SELECTED": "No parameter selected.",
"NO_PARAMETERS": "(no parameters)",
"RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN": "Return can only go in a method that returns a value",
"JUMP_CAN_ONLY_GO_IN_THEIR_STEPS_BLOCK": "Jump can only go in their step's block",
"STEP_DOES_NOT_EXIST_IN_STEPS": "Step \"%1\" does not exist in this steps block.",
"NO_STEP_SELECTED": "No step selected.",
Expand Down Expand Up @@ -577,6 +578,7 @@
"CALL_INSTANCE_METHOD_MISSING_CLASS": "This block calls a method in a class that no longer exists.",
"CALL_INSTANCE_VARIABLE_WRONG_TYPE": "The variable {{variableName}} doesn't always hold a {{className}}, so this block might not work.",
"SET_VARIABLE_DIFFERENT_TYPE": "This sets {{variableName}} to a value of type {{valueType}}, but elsewhere it is set to a value of type {{variableType}}.",
"RETURN_DIFFERENT_TYPES": "The values returned by this method have different types: {{types}}.",
"CALL_INSTANCE_METHOD_MISSING_METHOD": "This block calls a method that no longer exists.",
"CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD": "This block calls a method that does not exist in this module.",
"CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT": "This blocks calls a method on a private component in the {{mechanismClassName}} mechanism.",
Expand Down
2 changes: 2 additions & 0 deletions frontend/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
"PARAMETER_DOES_NOT_EXIST_IN_EVENT_HANDLER": "El parámetro \"%1\" no existe en este controlador de eventos.",
"NO_PARAMETER_SELECTED": "No se ha seleccionado ningún parámetro.",
"NO_PARAMETERS": "(sin parámetros)",
"RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN": "Devolver solo puede ir en un método que devuelve un valor",
"JUMP_CAN_ONLY_GO_IN_THEIR_STEPS_BLOCK": "El salto solo puede ir en el bloque de su paso",
"STEP_DOES_NOT_EXIST_IN_STEPS": "El paso \"%1\" no existe en este bloque de pasos.",
"NO_STEP_SELECTED": "No se ha seleccionado ningún paso.",
Expand Down Expand Up @@ -576,6 +577,7 @@
"CALL_INSTANCE_METHOD_MISSING_CLASS": "Este bloque llama a un método de una clase que ya no existe.",
"CALL_INSTANCE_VARIABLE_WRONG_TYPE": "La variable {{variableName}} no siempre contiene un {{className}}, por lo que este bloque podría no funcionar.",
"SET_VARIABLE_DIFFERENT_TYPE": "Esto asigna a {{variableName}} un valor de tipo {{valueType}}, pero en otro lugar se le asigna un valor de tipo {{variableType}}.",
"RETURN_DIFFERENT_TYPES": "Los valores devueltos por este método tienen tipos diferentes: {{types}}.",
"CALL_INSTANCE_METHOD_MISSING_METHOD": "Este bloque llama a un método que ya no existe.",
"CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD": "Este bloque llama a un método que no existe en este módulo.",
"CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT": "Este bloque llama a un método en un componente privado en el mecanismo {{mechanismClassName}}.",
Expand Down
2 changes: 2 additions & 0 deletions frontend/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
"PARAMETER_DOES_NOT_EXIST_IN_EVENT_HANDLER": "Le paramètre \"%1\" n'existe pas dans ce gestionnaire d'événements.",
"NO_PARAMETER_SELECTED": "Aucun paramètre sélectionné.",
"NO_PARAMETERS": "(aucun paramètre)",
"RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN": "Retourner ne peut aller que dans une méthode qui retourne une valeur",
"JUMP_CAN_ONLY_GO_IN_THEIR_STEPS_BLOCK": "Le saut ne peut aller que dans le bloc d'étapes correspondant",
"STEP_DOES_NOT_EXIST_IN_STEPS": "L'étape \"%1\" n'existe pas dans ce bloc d'étapes.",
"NO_STEP_SELECTED": "Aucune étape sélectionnée.",
Expand Down Expand Up @@ -578,6 +579,7 @@
"CALL_INSTANCE_METHOD_MISSING_CLASS": "Ce bloc appelle une méthode dans une classe qui n'existe plus.",
"CALL_INSTANCE_VARIABLE_WRONG_TYPE": "La variable {{variableName}} ne contient pas toujours un {{className}}, donc ce bloc risque de ne pas fonctionner.",
"SET_VARIABLE_DIFFERENT_TYPE": "Ceci donne à {{variableName}} une valeur de type {{valueType}}, mais ailleurs elle reçoit une valeur de type {{variableType}}.",
"RETURN_DIFFERENT_TYPES": "Les valeurs retournées par cette méthode ont des types différents : {{types}}.",
"CALL_INSTANCE_METHOD_MISSING_METHOD": "Ce bloc appelle une méthode qui n'existe plus.",
"CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD": "Ce bloc appelle une méthode qui n'existe pas dans ce module.",
"CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT": "Ce bloc appelle une méthode sur un composant privé dans le mécanisme {{mechanismClassName}}.",
Expand Down
2 changes: 2 additions & 0 deletions frontend/i18n/locales/he.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@
"PARAMETER_DOES_NOT_EXIST_IN_EVENT_HANDLER": "הפרמטר \"%1\" לא קיים במטפל אירועים זה.",
"NO_PARAMETER_SELECTED": "לא נבחר פרמטר.",
"NO_PARAMETERS": "(אין פרמטרים)",
"RETURN_CAN_ONLY_GO_IN_METHOD_WITH_RETURN": "החזרה יכולה להופיע רק בשיטה שמחזירה ערך",
"JUMP_CAN_ONLY_GO_IN_THEIR_STEPS_BLOCK": "קפיצה יכולה ללכת רק בבלוק הצעד שלה",
"STEP_DOES_NOT_EXIST_IN_STEPS": "הצעד \"%1\" לא קיים בבלוק הצעדים הזה.",
"NO_STEP_SELECTED": "לא נבחר צעד.",
Expand Down Expand Up @@ -575,6 +576,7 @@
"CALL_INSTANCE_METHOD_MISSING_CLASS": "בלוק זה קורא למתודה במחלקה שכבר לא קיימת.",
"CALL_INSTANCE_VARIABLE_WRONG_TYPE": "המשתנה {{variableName}} לא תמיד מכיל {{className}}, ולכן ייתכן שבלוק זה לא יעבוד.",
"SET_VARIABLE_DIFFERENT_TYPE": "בלוק זה מציב ב-{{variableName}} ערך מסוג {{valueType}}, אך במקום אחר מוצב בו ערך מסוג {{variableType}}.",
"RETURN_DIFFERENT_TYPES": "לערכים שמוחזרים על ידי שיטה זו יש סוגים שונים: {{types}}.",
"CALL_INSTANCE_METHOD_MISSING_METHOD": "בלוק זה קורא למתודה שכבר לא קיימת.",
"CALL_INSTANCE_WITHIN_METHOD_MISSING_METHOD": "בלוק זה קורא למתודה שאינה קיימת במודול זה.",
"CALL_COMPONENT_INSTANCE_METHOD_PRIVATE_COMPONENT": "בלוק זה קורא למתודה על רכיב פרטי במנגנון {{mechanismClassName}}.",
Expand Down
Loading