@@ -35,6 +35,8 @@ import * as toolboxItems from '../toolbox/items';
3535import { getClassData } from './utils/python' ;
3636import { FunctionData } from './utils/python_json_types' ;
3737import { findConnectedBlocksOfType } from './utils/find_connected_blocks' ;
38+ import { makeLegalName } from './utils/validator' ;
39+ import { NONCOPYABLE_BLOCK } from './noncopyable_block' ;
3840import { BLOCK_NAME as MRC_GET_PARAMETER_BLOCK_NAME } from './mrc_get_parameter' ;
3941import * as paramContainer from './mrc_param_container'
4042
@@ -57,7 +59,7 @@ export interface Parameter {
5759 type ?: string ;
5860}
5961
60- export type ClassMethodDefBlock = Blockly . Block & ClassMethodDefMixin & Blockly . BlockSvg ;
62+ export type ClassMethodDefBlock = Blockly . Block & ClassMethodDefMixin ;
6163interface ClassMethodDefMixin extends ClassMethodDefMixinType {
6264 mrcMethodId : string ,
6365 mrcCanChangeSignature : boolean ,
@@ -122,6 +124,7 @@ const CLASS_METHOD_DEF = {
122124 this . setNextStatement ( false ) ;
123125 this . updateBlock_ ( ) ;
124126 } ,
127+ ...NONCOPYABLE_BLOCK ,
125128 /**
126129 * Returns the state of this block as a JSON serializable object.
127130 */
@@ -180,12 +183,18 @@ const CLASS_METHOD_DEF = {
180183 if ( this . mrcCanChangeSignature ) {
181184 const nameField = new Blockly . FieldTextInput ( name ) ;
182185 input . insertFieldAt ( 0 , nameField , FIELD_METHOD_NAME ) ;
183- this . setMutator ( paramContainer . getMutatorIcon ( this ) ) ;
186+ if ( this . rendered ) {
187+ this . setMutator ( paramContainer . getMutatorIcon ( this as unknown as Blockly . BlockSvg ) ) ;
188+ }
184189 nameField . setValidator ( this . mrcNameFieldValidator . bind ( this , nameField ) ) ;
185190 } else {
186191 input . insertFieldAt ( 0 , createFieldNonEditableText ( name ) , FIELD_METHOD_NAME ) ;
187- // Case because a current bug in blockly where it won't allow passing null to Blockly.Block.setMutator makes it necessary.
188- ( this as Blockly . BlockSvg ) . setMutator ( null ) ;
192+ // Block.setMutator is defined as setMutator(_mutator: MutatorIcon) and BlockSvg.setMutator
193+ // is defined as setMutator(mutator: MutatorIcon | null).
194+ // Therefore, to call setMutator(null), this must be casted to BlockSvg.
195+ if ( this . rendered ) {
196+ ( this as unknown as Blockly . BlockSvg ) . setMutator ( null ) ;
197+ }
189198 }
190199 this . mrcUpdateParams ( ) ;
191200 this . mrcUpdateReturnInput ( ) ;
@@ -229,7 +238,9 @@ const CLASS_METHOD_DEF = {
229238 * mrcOnMutatorOpen is called when the mutator on a ClassMethodDefBlock is opened.
230239 */
231240 mrcOnMutatorOpen : function ( this : ClassMethodDefBlock ) : void {
232- paramContainer . onMutatorOpen ( this ) ;
241+ if ( this . rendered ) {
242+ paramContainer . onMutatorOpen ( this as unknown as Blockly . BlockSvg ) ;
243+ }
233244 } ,
234245 mrcRenameParameter : function ( this : ClassMethodDefBlock , oldName : string , newName : string ) {
235246 const nextBlock = this . getInputTargetBlock ( INPUT_STACK ) ;
@@ -283,10 +294,23 @@ const CLASS_METHOD_DEF = {
283294 // When the user changes the method name on the block, clear the mrcPythonMethodName field.
284295 this . mrcPythonMethodName = '' ;
285296
286- // Strip leading and trailing whitespace.
287- name = name . trim ( ) ;
297+ if ( this . isInFlyout ) {
298+ // Flyouts can have multiple methods with identical names.
299+ return name ;
300+ }
301+
302+ const otherNames : string [ ] = [ ] ;
303+ this . workspace . getBlocksByType ( BLOCK_NAME )
304+ . filter ( block => block . id !== this . id )
305+ . forEach ( ( block ) => {
306+ otherNames . push ( block . getFieldValue ( FIELD_METHOD_NAME ) ) ;
307+ const classMethodDefBlock = block as ClassMethodDefBlock ;
308+ if ( classMethodDefBlock . mrcPythonMethodName ) {
309+ otherNames . push ( classMethodDefBlock . mrcPythonMethodName ) ;
310+ }
311+ } ) ;
288312
289- const legalName = findLegalMethodName ( name , this ) ;
313+ const legalName = makeLegalName ( name , otherNames , /* mustBeValidPythonIdentifier */ true ) ;
290314 const oldName = nameField . getValue ( ) ;
291315 if ( oldName && oldName !== name && oldName !== legalName ) {
292316 // Rename any callers.
@@ -364,61 +388,6 @@ const CLASS_METHOD_DEF = {
364388 } ,
365389} ;
366390
367- /**
368- * Ensure two identically-named methods don't exist.
369- * Take the proposed method name, and return a legal name i.e. one that
370- * is not empty and doesn't collide with other methods.
371- *
372- * @param name Proposed method name.
373- * @param block Block to disambiguate.
374- * @returns Non-colliding name.
375- */
376- function findLegalMethodName ( name : string , block : ClassMethodDefBlock ) : string {
377- if ( block . isInFlyout ) {
378- // Flyouts can have multiple methods called 'my_method'.
379- return name ;
380- }
381- name = name || 'unnamed' ;
382- while ( isMethodNameUsed ( name , block . workspace , block ) ) {
383- // Collision with another method.
384- const r = name . match ( / ^ ( .* ?) ( \d + ) $ / ) ;
385- if ( ! r ) {
386- name += '2' ;
387- } else {
388- name = r [ 1 ] + ( parseInt ( r [ 2 ] ) + 1 ) ;
389- }
390- }
391- return name ;
392- }
393-
394- /**
395- * Return if the given name is already a method name.
396- *
397- * @param name The questionable name.
398- * @param workspace The workspace to scan for collisions.
399- * @param opt_exclude Optional block to exclude from comparisons (one doesn't
400- * want to collide with oneself).
401- * @returns True if the name is used, otherwise return false.
402- */
403- function isMethodNameUsed (
404- name : string , workspace : Blockly . Workspace , opt_exclude ?: Blockly . Block ) : boolean {
405- const nameLowerCase = name . toLowerCase ( ) ;
406- for ( const block of workspace . getBlocksByType ( BLOCK_NAME ) ) {
407- if ( block === opt_exclude ) {
408- continue ;
409- }
410- if ( nameLowerCase === block . getFieldValue ( FIELD_METHOD_NAME ) . toLowerCase ( ) ) {
411- return true ;
412- }
413- const classMethodDefBlock = block as ClassMethodDefBlock ;
414- if ( classMethodDefBlock . mrcPythonMethodName &&
415- nameLowerCase === classMethodDefBlock . mrcPythonMethodName . toLowerCase ( ) ) {
416- return true ;
417- }
418- }
419- return false ;
420- }
421-
422391export const setup = function ( ) {
423392 Blockly . Blocks [ BLOCK_NAME ] = CLASS_METHOD_DEF ;
424393} ;
0 commit comments