Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
181 changes: 133 additions & 48 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { MRC_STYLE_FUNCTIONS } from '../themes/styles'
import { ClassMethodDefExtraState } from './mrc_class_method_def'

// A block to call a python function.

Expand Down Expand Up @@ -57,7 +58,8 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
mrcImportModule: string,
mrcActualFunctionName: string,
mrcExportedFunction: boolean,
maybeRenameProcedure(this: CallPythonFunctionBlock, oldName: string, legalName: string): void;
renameMethod(this: CallPythonFunctionBlock, oldName: string, newName: string): void;
mutateMethod(this: CallPythonFunctionBlock, defBlockExtraState: ClassMethodDefExtraState): void;
}
type CallPythonFunctionMixinType = typeof CALL_PYTHON_FUNCTION;

Expand Down Expand Up @@ -219,59 +221,95 @@ const CALL_PYTHON_FUNCTION = {
this.setNextStatement(true, null);
this.setOutput(false);
}
// Add the dummy input.
switch (this.mrcFunctionKind) {
case FunctionKind.MODULE:
this.appendDummyInput()
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.STATIC:
this.appendDummyInput()
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.CONSTRUCTOR:
this.appendDummyInput()
.appendField('create')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME);
break;
case FunctionKind.INSTANCE:
this.appendDummyInput()
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.INSTANCE_WITHIN:
this.appendDummyInput()
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
default:
throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind)

if (!this.getInput('TITLE')) {
// Add the dummy input.
switch (this.mrcFunctionKind) {
case FunctionKind.MODULE:
this.appendDummyInput('TITLE')
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.STATIC:
this.appendDummyInput('TITLE')
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.CONSTRUCTOR:
this.appendDummyInput('TITLE')
.appendField('create')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME);
break;
case FunctionKind.INSTANCE:
this.appendDummyInput('TITLE')
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_MODULE_OR_CLASS_NAME)
.appendField('.')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
break;
case FunctionKind.INSTANCE_WITHIN: {
const input = this.getInput('TITLE');
if (!input) {
this.appendDummyInput('TITLE')
.appendField('call')
.appendField(createFieldNonEditableText(''), pythonUtils.FIELD_FUNCTION_NAME);
}
break;
}
default:
throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind)
}
}
// Add input sockets for the arguments.

// Update input sockets for the arguments.
for (let i = 0; i < this.mrcArgs.length; i++) {
const input = this.appendValueInput('ARG' + i)
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(this.mrcArgs[i].name);
const argName = this.mrcArgs[i].name;
let argInput = this.getInput('ARG' + i);
const argField = this.getField('ARGNAME' + i);
if (argInput && argField) {
// Ensure argument name is up to date. No need to fire a change event.
Blockly.Events.disable();
try {
argField.setValue(argName);
} finally {
Blockly.Events.enable();
}
} else {
// Add new input.
argInput = this.appendValueInput('ARG' + i)
.setAlign(Blockly.inputs.Align.RIGHT)
.appendField(argName, 'ARGNAME' + i);
}
if (this.mrcArgs[i].type) {
input.setCheck(getAllowedTypesForSetCheck(this.mrcArgs[i].type));
argInput.setCheck(getAllowedTypesForSetCheck(this.mrcArgs[i].type));
}
}
},
maybeRenameProcedure: function(this: CallPythonFunctionBlock, oldName: string, newName: string): void {
if (this.mrcFunctionKind === FunctionKind.INSTANCE_WITHIN) {
if (this.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME) == oldName) {
this.setFieldValue(newName, pythonUtils.FIELD_FUNCTION_NAME);
}
// Remove deleted inputs.
for (let i = this.mrcArgs.length; this.getInput('ARG' + i); i++) {
this.removeInput('ARG' + i);
}
}
},
renameMethod: function(this: CallPythonFunctionBlock, newName: string): void {
this.setFieldValue(newName, pythonUtils.FIELD_FUNCTION_NAME);
},
mutateMethod: function(
this: CallPythonFunctionBlock,
defBlockExtraState: ClassMethodDefExtraState
): void {
this.mrcReturnType = defBlockExtraState.returnType;
this.mrcArgs = [];
defBlockExtraState.params.forEach((param) => {
this.mrcArgs.push({
'name': param.name,
'type': param.type,
});
});
this.updateBlock_();
},
};

export const setup = function() {
Expand Down Expand Up @@ -356,3 +394,50 @@ function generateCodeForArguments(
}
return code;
}

function getMethodCallers(workspace: Blockly.Workspace, name: string): Blockly.Block[] {
return workspace.getBlocksByType('mrc_call_python_function').filter((block) => {
const callBlock = block as CallPythonFunctionBlock;
return (
callBlock.mrcFunctionKind === FunctionKind.INSTANCE_WITHIN &&
callBlock.getFieldValue(pythonUtils.FIELD_FUNCTION_NAME) === name
);
});
}

export function renameMethodCallers(workspace: Blockly.Workspace, oldName: string, newName: string): void {
for (const block of getMethodCallers(workspace, oldName)) {
(block as CallPythonFunctionBlock).renameMethod(newName);
}
}

export function mutateMethodCallers(
workspace: Blockly.Workspace, methodName: string, defBlockExtraState: ClassMethodDefExtraState) {
const oldRecordUndo = Blockly.Events.getRecordUndo();

for (const block of getMethodCallers(workspace, methodName)) {
const callBlock = block as CallPythonFunctionBlock;
// Get the extra state before changing the call block.
const oldExtraState = callBlock.saveExtraState();

// Apply the changes.
callBlock.mutateMethod(defBlockExtraState);

// Get the extra state after changing the call block.
const newExtraState = callBlock.saveExtraState();
if (oldExtraState !== newExtraState) {
// Fire a change event, but don't record it as an undoable action.
Blockly.Events.setRecordUndo(false);
Blockly.Events.fire(
new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))(
callBlock,
'mutation',
null,
oldExtraState,
newExtraState,
),
);
Blockly.Events.setRecordUndo(oldRecordUndo);
}
}
}
9 changes: 4 additions & 5 deletions src/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import * as ChangeFramework from './utils/change_framework'
import { getLegalName } from './utils/python';
import { Order } from 'blockly/python';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { CallPythonFunctionBlock } from './mrc_call_python_function'
import { renameMethodCallers, mutateMethodCallers } from './mrc_call_python_function'

export const BLOCK_NAME = 'mrc_class_method_def';

Expand Down Expand Up @@ -146,6 +146,7 @@ const CLASS_METHOD_DEF = {
});
});
this.updateBlock_();
mutateMethodCallers(this.workspace, this.getFieldValue('NAME'), this.saveExtraState());
},
/**
* Update the block to reflect the newly loaded extra state.
Expand Down Expand Up @@ -186,7 +187,7 @@ const CLASS_METHOD_DEF = {
paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
}
this.mrcUpdateParams();
//Blockly.Procedures.mutateCallers(this);
mutateMethodCallers(this.workspace, this.getFieldValue('NAME'), this.saveExtraState());
},
decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) {
// This is a special sub-block that only gets created in the mutator UI.
Expand Down Expand Up @@ -238,9 +239,7 @@ const CLASS_METHOD_DEF = {
const oldName = nameField.getValue();
if (oldName !== name && oldName !== legalName) {
// Rename any callers.
for (const block of this.workspace.getBlocksByType('mrc_call_python_function')) {
(block as CallPythonFunctionBlock).maybeRenameProcedure(oldName, legalName);
}
renameMethodCallers(this.workspace, oldName, legalName);
}
return legalName;
},
Expand Down