@@ -26,6 +26,7 @@ import * as ChangeFramework from './utils/change_framework'
2626import { getLegalName } from './utils/python' ;
2727import { Order } from 'blockly/python' ;
2828import { ExtendedPythonGenerator } from '../editor/extended_python_generator' ;
29+ import { CallPythonFunctionBlock } from './mrc_call_python_function'
2930
3031export const BLOCK_NAME = 'mrc_class_method_def' ;
3132
@@ -158,8 +159,10 @@ const CLASS_METHOD_DEF = {
158159 input . removeField ( 'NAME' ) ;
159160
160161 if ( this . mrcCanChangeSignature ) {
161- input . insertFieldAt ( 0 , new Blockly . FieldTextInput ( name ) , 'NAME' ) ;
162+ const nameField = new Blockly . FieldTextInput ( name ) ;
163+ input . insertFieldAt ( 0 , nameField , 'NAME' ) ;
162164 this . setMutator ( new Blockly . icons . MutatorIcon ( [ MUTATOR_BLOCK_NAME ] , this ) ) ;
165+ nameField . setValidator ( this . mrcNameFieldValidator . bind ( this , nameField ) ) ;
163166 }
164167 else {
165168 input . insertFieldAt ( 0 , createFieldNonEditableText ( name ) , 'NAME' ) ;
@@ -224,8 +227,79 @@ const CLASS_METHOD_DEF = {
224227 Blockly . Events . enable ( ) ;
225228 }
226229 } ,
230+ mrcNameFieldValidator ( this : ClassMethodDefBlock , nameField : Blockly . FieldTextInput , name : string ) : string {
231+ // When the user changes the method name on the block, clear the mrcPythonMethodName field.
232+ this . mrcPythonMethodName = '' ;
233+
234+ // Strip leading and trailing whitespace.
235+ name = name . trim ( ) ;
236+
237+ const legalName = findLegalMethodName ( name , this ) ;
238+ const oldName = nameField . getValue ( ) ;
239+ if ( oldName !== name && oldName !== legalName ) {
240+ // Rename any callers.
241+ for ( const block of this . workspace . getBlocksByType ( 'mrc_call_python_function' ) ) {
242+ ( block as CallPythonFunctionBlock ) . maybeRenameProcedure ( oldName , legalName ) ;
243+ }
244+ }
245+ return legalName ;
246+ } ,
227247} ;
228248
249+ /**
250+ * Ensure two identically-named methods don't exist.
251+ * Take the proposed method name, and return a legal name i.e. one that
252+ * is not empty and doesn't collide with other methods.
253+ *
254+ * @param name Proposed method name.
255+ * @param block Block to disambiguate.
256+ * @returns Non-colliding name.
257+ */
258+ function findLegalMethodName ( name : string , block : ClassMethodDefBlock ) : string {
259+ if ( block . isInFlyout ) {
260+ // Flyouts can have multiple methods called 'my_method'.
261+ return name ;
262+ }
263+ name = name || 'unnamed' ;
264+ while ( isMethodNameUsed ( name , block . workspace , block ) ) {
265+ // Collision with another method.
266+ const r = name . match ( / ^ ( .* ?) ( \d + ) $ / ) ;
267+ if ( ! r ) {
268+ name += '2' ;
269+ } else {
270+ name = r [ 1 ] + ( parseInt ( r [ 2 ] ) + 1 ) ;
271+ }
272+ }
273+ return name ;
274+ }
275+
276+ /**
277+ * Return if the given name is already a method name.
278+ *
279+ * @param name The questionable name.
280+ * @param workspace The workspace to scan for collisions.
281+ * @param opt_exclude Optional block to exclude from comparisons (one doesn't
282+ * want to collide with oneself).
283+ * @returns True if the name is used, otherwise return false.
284+ */
285+ function isMethodNameUsed (
286+ name : string , workspace : Workspace , opt_exclude ?: Block ) : boolean {
287+ const nameLowerCase = name . toLowerCase ( ) ;
288+ for ( const block of workspace . getBlocksByType ( 'mrc_class_method_def' ) ) {
289+ if ( block === opt_exclude ) {
290+ continue ;
291+ }
292+ if ( nameLowerCase === block . getFieldValue ( 'NAME' ) . toLowerCase ( ) ) {
293+ return true ;
294+ }
295+ if ( block . mrcPythonMethodName &&
296+ nameLowerCase === block . mrcPythonMethodName . toLowerCase ( ) ) {
297+ return true ;
298+ }
299+ }
300+ return false ;
301+ }
302+
229303const METHOD_PARAM_CONTAINER = {
230304 init : function ( this : Blockly . Block ) {
231305 this . appendDummyInput ( "TITLE" ) . appendField ( 'Parameters' ) ;
@@ -421,7 +495,7 @@ export const pythonFromBlock = function (
421495 xfix2 +
422496 returnValue ;
423497 code = generator . scrub_ ( block , code ) ;
424- generator . addClassMethodDefinition ( block . getFieldValue ( 'NAME' ) , funcName , code ) ;
498+ generator . addClassMethodDefinition ( funcName , code ) ;
425499
426500 return '' ;
427- }
501+ }
0 commit comments