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
18 changes: 18 additions & 0 deletions blocks/procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ const procedureDefGetDefMixin = function() {
return this.model_;
},

/**
* True if this is a procedure definition block, false otherwise (i.e.
* it is a caller).
* @return {boolean} True because this is a procedure definition block.
*/
isProcedureDef() {
return true;
},

/**
* Return all variables referenced by this block.
* @return {!Array<string>} List of variable names.
Expand Down Expand Up @@ -997,6 +1006,15 @@ const procedureCallerGetDefMixin = function() {
return /** @type {string} */ (this.getFieldValue('NAME'));
},

/**
* True if this is a procedure definition block, false otherwise (i.e.
* it is a caller).
* @return {boolean} False because this is not a procedure definition block.
*/
isProcedureDef() {
return false;
},

/**
* Return all variables referenced by this block.
* @return {!Array<string>} List of variable names.
Expand Down
7 changes: 5 additions & 2 deletions core/interfaces/i_procedure_block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import {IProcedureModel} from './i_procedure_model.js';

/** The interface for a block which models a procedure. */
export interface IProcedureBlock {
doProcedureUpdate(): void;
getProcedureModel(): IProcedureModel;
doProcedureUpdate(): void;
isProcedureDef(): boolean;
}

/** A type guard which checks if the given block is a procedure block. */
export function isProcedureBlock(block: Block|
IProcedureBlock): block is IProcedureBlock {
return (block as IProcedureBlock).doProcedureUpdate !== undefined;
return (block as IProcedureBlock).getProcedureModel !== undefined &&
(block as IProcedureBlock).doProcedureUpdate !== undefined &&
(block as IProcedureBlock).isProcedureDef !== undefined;
}
130 changes: 84 additions & 46 deletions core/procedures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ export interface ProcedureBlock {
getProcedureDef: () => ProcedureTuple;
}

interface LegacyProcedureDefBlock {
getProcedureDef: () => ProcedureTuple
}

function isLegacyProcedureDefBlock(block: Object):
block is LegacyProcedureDefBlock {
return (block as any).getProcedureDef !== undefined;
}

interface LegacyProcedureCallBlock {
getProcedureCall: () => string;
renameProcedure: (p1: string, p2: string) => void;
}

function isLegacyProcedureCallBlock(block: Object):
block is LegacyProcedureCallBlock {
return (block as any).getProcedureCall !== undefined &&
(block as any).renameProcedure !== undefined;
}

/**
* Find all user-created procedure definitions in a workspace.
*
Expand All @@ -78,15 +98,37 @@ export interface ProcedureBlock {
*/
export function allProcedures(root: Workspace):
[ProcedureTuple[], ProcedureTuple[]] {
const proceduresNoReturn =
root.getBlocksByType('procedures_defnoreturn', false)
.map(function(block) {
return (block as unknown as ProcedureBlock).getProcedureDef();
});
const proceduresReturn =
root.getBlocksByType('procedures_defreturn', false).map(function(block) {
return (block as unknown as ProcedureBlock).getProcedureDef();
});
const proceduresNoReturn: ProcedureTuple[] =
root.getProcedureMap()
.getProcedures()
.filter((p) => !p.getReturnTypes())
.map(
(p) =>
[p.getName(),
p.getParameters().map((pa) => pa.getName()),
false,
]);
root.getBlocksByType('procedures_defnoreturn', false).forEach((b) => {
if (!isProcedureBlock(b) && isLegacyProcedureDefBlock(b)) {
proceduresNoReturn.push(b.getProcedureDef());
}
});

const proceduresReturn: ProcedureTuple[] =
root.getProcedureMap()
.getProcedures()
.filter((p) => !!p.getReturnTypes())
.map(
(p) =>
[p.getName(),
p.getParameters().map((pa) => pa.getName()),
true,
]);
root.getBlocksByType('procedures_defreturn', false).forEach((b) => {
if (!isProcedureBlock(b) && isLegacyProcedureDefBlock(b)) {
proceduresReturn.push(b.getProcedureDef());
}
});
proceduresNoReturn.sort(procTupleComparator);
proceduresReturn.sort(procTupleComparator);
return [proceduresNoReturn, proceduresReturn];
Expand Down Expand Up @@ -158,19 +200,16 @@ function isLegalName(
*/
export function isNameUsed(
name: string, workspace: Workspace, opt_exclude?: Block): boolean {
const blocks = workspace.getAllBlocks(false);
// Iterate through every block and check the name.
for (let i = 0; i < blocks.length; i++) {
if (blocks[i] === opt_exclude) {
continue;
for (const block of workspace.getAllBlocks(false)) {
if (block === opt_exclude) continue;

if (isProcedureBlock(block) && block.isProcedureDef() &&
Names.equals(block.getProcedureModel().getName(), name)) {
return true;
}
// Assume it is a procedure block so we can check.
const procedureBlock = blocks[i] as unknown as ProcedureBlock;
if (procedureBlock.getProcedureDef) {
const procName = procedureBlock.getProcedureDef();
if (Names.equals(procName[0], name)) {
return true;
}
if (isLegacyProcedureDefBlock(block) &&
Names.equals(block.getProcedureDef()[0], name)) {
return true;
}
}
return false;
Expand Down Expand Up @@ -382,21 +421,21 @@ function mutatorChangeListener(e: Abstract) {
* @alias Blockly.Procedures.getCallers
*/
export function getCallers(name: string, workspace: Workspace): Block[] {
const callers = [];
const blocks = workspace.getAllBlocks(false);
// Iterate through every block and check the name.
for (let i = 0; i < blocks.length; i++) {
// Assume it is a procedure block so we can check.
const procedureBlock = blocks[i] as unknown as ProcedureBlock;
if (procedureBlock.getProcedureCall) {
const procName = procedureBlock.getProcedureCall();
// Procedure name may be null if the block is only half-built.
if (procName && Names.equals(procName, name)) {
callers.push(blocks[i]);
}
}
}
return callers;
return workspace.getAllBlocks(false).filter((block) => {
return blockIsModernCallerFor(block, name) ||
(isLegacyProcedureCallBlock(block) &&
Names.equals(block.getProcedureCall(), name));
});
}

/**
* @returns True if the given block is a modern-style caller block of the given
* procedure name.
*/
function blockIsModernCallerFor(block: Block, procName: string): boolean {
return isProcedureBlock(block) && !block.isProcedureDef() &&
block.getProcedureModel() &&
Names.equals(block.getProcedureModel().getName(), procName);
}

/**
Expand Down Expand Up @@ -443,16 +482,15 @@ export function mutateCallers(defBlock: Block) {
export function getDefinition(name: string, workspace: Workspace): Block|null {
// Do not assume procedure is a top block. Some languages allow nested
// procedures. Also do not assume it is one of the built-in blocks. Only
// rely on getProcedureDef.
const blocks = workspace.getAllBlocks(false);
for (let i = 0; i < blocks.length; i++) {
// Assume it is a procedure block so we can check.
const procedureBlock = blocks[i] as unknown as ProcedureBlock;
if (procedureBlock.getProcedureDef) {
const tuple = procedureBlock.getProcedureDef();
if (tuple && Names.equals(tuple[0], name)) {
return blocks[i]; // Can't use procedureBlock var due to type check.
}
// rely on isProcedureDef and getProcedureDef.
for (const block of workspace.getAllBlocks(false)) {
if (isProcedureBlock(block) && block.isProcedureDef() &&
Names.equals(block.getProcedureModel().getName(), name)) {
return block;
}
if (isLegacyProcedureDefBlock(block) &&
Names.equals(block.getProcedureDef()[0], name)) {
return block;
}
}
return null;
Expand Down
2 changes: 2 additions & 0 deletions tests/mocha/procedure_map_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ suite('Procedure Map', function() {
Blockly.Blocks['procedure_mock'] = {
init: function() { },
doProcedureUpdate: function() { },
getProcedureModel: function() { },
isProcedureDef: function() { },
};

this.procedureBlock = this.workspace.newBlock('procedure_mock');
Expand Down