@@ -28,6 +28,7 @@ import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
2828import { getAllowedTypesForSetCheck , getOutputCheck } from './utils/python' ;
2929import { ExtendedPythonGenerator } from '../editor/extended_python_generator' ;
3030import { MRC_STYLE_FUNCTIONS } from '../themes/styles'
31+ import { ClassMethodDefExtraState } from './mrc_class_method_def'
3132
3233// A block to call a python function.
3334
@@ -57,7 +58,8 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
5758 mrcImportModule : string ,
5859 mrcActualFunctionName : string ,
5960 mrcExportedFunction : boolean ,
60- maybeRenameProcedure ( this : CallPythonFunctionBlock , oldName : string , legalName : string ) : void ;
61+ renameMethod ( this : CallPythonFunctionBlock , oldName : string , newName : string ) : void ;
62+ mutateMethod ( this : CallPythonFunctionBlock , defBlockExtraState : ClassMethodDefExtraState ) : void ;
6163}
6264type CallPythonFunctionMixinType = typeof CALL_PYTHON_FUNCTION ;
6365
@@ -219,59 +221,95 @@ const CALL_PYTHON_FUNCTION = {
219221 this . setNextStatement ( true , null ) ;
220222 this . setOutput ( false ) ;
221223 }
222- // Add the dummy input.
223- switch ( this . mrcFunctionKind ) {
224- case FunctionKind . MODULE :
225- this . appendDummyInput ( )
226- . appendField ( 'call' )
227- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
228- . appendField ( '.' )
229- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
230- break ;
231- case FunctionKind . STATIC :
232- this . appendDummyInput ( )
233- . appendField ( 'call' )
234- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
235- . appendField ( '.' )
236- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
237- break ;
238- case FunctionKind . CONSTRUCTOR :
239- this . appendDummyInput ( )
240- . appendField ( 'create' )
241- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME ) ;
242- break ;
243- case FunctionKind . INSTANCE :
244- this . appendDummyInput ( )
245- . appendField ( 'call' )
246- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
247- . appendField ( '.' )
248- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
249- break ;
250- case FunctionKind . INSTANCE_WITHIN :
251- this . appendDummyInput ( )
252- . appendField ( 'call' )
253- . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
254- break ;
255- default :
256- throw new Error ( 'mrcFunctionKind has unexpected value: ' + mrcFunctionKind )
224+
225+ if ( ! this . getInput ( 'TITLE' ) ) {
226+ // Add the dummy input.
227+ switch ( this . mrcFunctionKind ) {
228+ case FunctionKind . MODULE :
229+ this . appendDummyInput ( 'TITLE' )
230+ . appendField ( 'call' )
231+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
232+ . appendField ( '.' )
233+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
234+ break ;
235+ case FunctionKind . STATIC :
236+ this . appendDummyInput ( 'TITLE' )
237+ . appendField ( 'call' )
238+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
239+ . appendField ( '.' )
240+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
241+ break ;
242+ case FunctionKind . CONSTRUCTOR :
243+ this . appendDummyInput ( 'TITLE' )
244+ . appendField ( 'create' )
245+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME ) ;
246+ break ;
247+ case FunctionKind . INSTANCE :
248+ this . appendDummyInput ( 'TITLE' )
249+ . appendField ( 'call' )
250+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_MODULE_OR_CLASS_NAME )
251+ . appendField ( '.' )
252+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
253+ break ;
254+ case FunctionKind . INSTANCE_WITHIN : {
255+ const input = this . getInput ( 'TITLE' ) ;
256+ if ( ! input ) {
257+ this . appendDummyInput ( 'TITLE' )
258+ . appendField ( 'call' )
259+ . appendField ( createFieldNonEditableText ( '' ) , pythonUtils . FIELD_FUNCTION_NAME ) ;
260+ }
261+ break ;
262+ }
263+ default :
264+ throw new Error ( 'mrcFunctionKind has unexpected value: ' + mrcFunctionKind )
265+ }
257266 }
258- // Add input sockets for the arguments.
267+
268+ // Update input sockets for the arguments.
259269 for ( let i = 0 ; i < this . mrcArgs . length ; i ++ ) {
260- const input = this . appendValueInput ( 'ARG' + i )
261- . setAlign ( Blockly . inputs . Align . RIGHT )
262- . appendField ( this . mrcArgs [ i ] . name ) ;
270+ const argName = this . mrcArgs [ i ] . name ;
271+ let argInput = this . getInput ( 'ARG' + i ) ;
272+ const argField = this . getField ( 'ARGNAME' + i ) ;
273+ if ( argInput && argField ) {
274+ // Ensure argument name is up to date. No need to fire a change event.
275+ Blockly . Events . disable ( ) ;
276+ try {
277+ argField . setValue ( argName ) ;
278+ } finally {
279+ Blockly . Events . enable ( ) ;
280+ }
281+ } else {
282+ // Add new input.
283+ argInput = this . appendValueInput ( 'ARG' + i )
284+ . setAlign ( Blockly . inputs . Align . RIGHT )
285+ . appendField ( argName , 'ARGNAME' + i ) ;
286+ }
263287 if ( this . mrcArgs [ i ] . type ) {
264- input . setCheck ( getAllowedTypesForSetCheck ( this . mrcArgs [ i ] . type ) ) ;
288+ argInput . setCheck ( getAllowedTypesForSetCheck ( this . mrcArgs [ i ] . type ) ) ;
265289 }
266290 }
267- } ,
268- maybeRenameProcedure : function ( this : CallPythonFunctionBlock , oldName : string , newName : string ) : void {
269- if ( this . mrcFunctionKind === FunctionKind . INSTANCE_WITHIN ) {
270- if ( this . getFieldValue ( pythonUtils . FIELD_FUNCTION_NAME ) == oldName ) {
271- this . setFieldValue ( newName , pythonUtils . FIELD_FUNCTION_NAME ) ;
272- }
291+ // Remove deleted inputs.
292+ for ( let i = this . mrcArgs . length ; this . getInput ( 'ARG' + i ) ; i ++ ) {
293+ this . removeInput ( 'ARG' + i ) ;
273294 }
274- }
295+ } ,
296+ renameMethod : function ( this : CallPythonFunctionBlock , newName : string ) : void {
297+ this . setFieldValue ( newName , pythonUtils . FIELD_FUNCTION_NAME ) ;
298+ } ,
299+ mutateMethod : function (
300+ this : CallPythonFunctionBlock ,
301+ defBlockExtraState : ClassMethodDefExtraState
302+ ) : void {
303+ this . mrcReturnType = defBlockExtraState . returnType ;
304+ this . mrcArgs = [ ] ;
305+ defBlockExtraState . params . forEach ( ( param ) => {
306+ this . mrcArgs . push ( {
307+ 'name' : param . name ,
308+ 'type' : param . type ,
309+ } ) ;
310+ } ) ;
311+ this . updateBlock_ ( ) ;
312+ } ,
275313} ;
276314
277315export const setup = function ( ) {
@@ -356,3 +394,50 @@ function generateCodeForArguments(
356394 }
357395 return code ;
358396}
397+
398+ function getMethodCallers ( workspace : Blockly . Workspace , name : string ) : Blockly . Block [ ] {
399+ return workspace . getBlocksByType ( 'mrc_call_python_function' ) . filter ( ( block ) => {
400+ const callBlock = block as CallPythonFunctionBlock ;
401+ return (
402+ callBlock . mrcFunctionKind === FunctionKind . INSTANCE_WITHIN &&
403+ callBlock . getFieldValue ( pythonUtils . FIELD_FUNCTION_NAME ) === name
404+ ) ;
405+ } ) ;
406+ }
407+
408+ export function renameMethodCallers ( workspace : Blockly . Workspace , oldName : string , newName : string ) : void {
409+ for ( const block of getMethodCallers ( workspace , oldName ) ) {
410+ ( block as CallPythonFunctionBlock ) . renameMethod ( newName ) ;
411+ }
412+ }
413+
414+ export function mutateMethodCallers (
415+ workspace : Blockly . Workspace , methodName : string , defBlockExtraState : ClassMethodDefExtraState ) {
416+ const oldRecordUndo = Blockly . Events . getRecordUndo ( ) ;
417+
418+ for ( const block of getMethodCallers ( workspace , methodName ) ) {
419+ const callBlock = block as CallPythonFunctionBlock ;
420+ // Get the extra state before changing the call block.
421+ const oldExtraState = callBlock . saveExtraState ( ) ;
422+
423+ // Apply the changes.
424+ callBlock . mutateMethod ( defBlockExtraState ) ;
425+
426+ // Get the extra state after changing the call block.
427+ const newExtraState = callBlock . saveExtraState ( ) ;
428+ if ( oldExtraState !== newExtraState ) {
429+ // Fire a change event, but don't record it as an undoable action.
430+ Blockly . Events . setRecordUndo ( false ) ;
431+ Blockly . Events . fire (
432+ new ( Blockly . Events . get ( Blockly . Events . BLOCK_CHANGE ) ) (
433+ callBlock ,
434+ 'mutation' ,
435+ null ,
436+ oldExtraState ,
437+ newExtraState ,
438+ ) ,
439+ ) ;
440+ Blockly . Events . setRecordUndo ( oldRecordUndo ) ;
441+ }
442+ }
443+ }
0 commit comments