Skip to content

Commit 8cc3d5a

Browse files
committed
HeyLiz
1 parent 0e19d63 commit 8cc3d5a

File tree

4 files changed

+156
-52
lines changed

4 files changed

+156
-52
lines changed

src/editor/editor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export class Editor {
195195
return;
196196
}
197197
const exportedBlocks = commonStorage.extractExportedBlocks(
198-
this.currentModule.projectName, this.projectContent);
198+
this.projectPath, this.projectContent);
199199
this.setToolbox(getToolboxJSON(exportedBlocks, shownPythonToolboxCategories));
200200
}
201201
}

src/editor/extended_python_generator.ts

Lines changed: 150 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ import { Block } from '../toolbox/items';
2626
import { FunctionArg } from '../blocks/mrc_call_python_function';
2727
import * as commonStorage from '../storage/common_storage';
2828

29+
30+
const CLASS_NAME_PLACEHOLDER = '%class_name%';
31+
const FULL_CLASS_NAME_PLACEHOLDER = '%full_class_name%';
32+
const INSTANCE_LABEL_PLACEHOLDER = '%instance_label%';
33+
const INSTANCE_VARIABLE_NAME_PLACEHOLDER = '%instance_var_name%';
34+
2935
// Extends the python generator to collect some information about functions and
3036
// variables that have been defined so they can be used in other modules.
3137

@@ -123,53 +129,60 @@ export class ExtendedPythonGenerator extends PythonGenerator {
123129
}
124130

125131
private produceExportedBlocks(workspace: Blockly.Workspace): Block[] {
126-
// The exported blocks produced here have the extraState.importModule and fields.MODULE values
127-
// set to the MODULE_NAME_PLACEHOLDER. This is so blocks modules can be renamed and copied
128-
// without having to change the contents of the modules.
129-
// The placeholders will be replaced with the actual module name before they are added to the
130-
// toolbox.
132+
// The exported blocks produced here have some values set to placeholders.
133+
// This is so blocks modules can be renamed and copied without having to change the
134+
// contents of the modules. The placeholders will be replaced with the actual module
135+
// name before they are added to the toolbox. See function replacePlaceholders below.
131136

132137
const exportedBlocks = [];
133138

134-
// All functions are exported.
135-
// TODO(lizlooney): instead of looking at procedure blocks, this code needs
136-
// to look at mrc_class_method_def blocks.
137-
const allProcedures = Blockly.Procedures.allProcedures(workspace);
138-
const procedureTuples = allProcedures[0].concat(allProcedures[1]);
139-
for (const procedureTuple of procedureTuples) {
140-
const functionName = procedureTuple[0];
141-
const blockDefinition = Blockly.Procedures.getDefinition(functionName, workspace);
142-
if (!blockDefinition || !blockDefinition.isEnabled()) {
143-
continue;
139+
// Look at mrc_class_method_def blocks and make corresponding mrc_call_python_function blocks.
140+
workspace.getBlocksByType('mrc_class_method_def').forEach((classMethodDefBlock) => {
141+
if (!classMethodDefBlock.mrcCanBeCalledOutsideClass ||
142+
!classMethodDefBlock.isEnabled()) {
143+
return;
144144
}
145-
const actualFunctionName = super.getProcedureName(functionName);
146-
const hasReturnValue = procedureTuple[2];
147145
const args: FunctionArg[] = [];
148-
const parameterNames = procedureTuple[1];
149-
parameterNames.forEach((parameterName) => {
146+
args.push({
147+
'name': INSTANCE_LABEL_PLACEHOLDER,
148+
'type': FULL_CLASS_NAME_PLACEHOLDER,
149+
});
150+
classMethodDefBlock.mrcParameters.forEach((param) => {
150151
args.push({
151-
'name': parameterName,
152-
'type': '',
153-
})
152+
'name': param.name,
153+
'type': param.type,
154+
});
154155
});
155156
const callFunctionBlock: Block = {
156157
'kind': 'block',
157158
'type': 'mrc_call_python_function',
158159
'extraState': {
159-
'functionKind': 'module',
160-
'returnType': hasReturnValue ? '' : 'None',
160+
'functionKind': 'instance',
161+
'returnType': classMethodDefBlock.mrcPythonMethodName,
161162
'args': args,
162-
'importModule': commonStorage.MODULE_NAME_PLACEHOLDER,
163-
'actualFunctionName': actualFunctionName,
163+
'importModule': '',
164+
'actualFunctionName': classMethodDefBlock.mrcPythonMethodName,
164165
'exportedFunction': true,
165166
},
166167
'fields': {
167-
'MODULE_OR_CLASS': commonStorage.MODULE_NAME_PLACEHOLDER,
168-
'FUNC': functionName,
168+
'MODULE_OR_CLASS': FULL_CLASS_NAME_PLACEHOLDER,
169+
'FUNC': classMethodDefBlock.getFieldValue('NAME'),
169170
},
171+
'inputs': {
172+
'ARG0': {
173+
'block': {
174+
'type': 'variables_get',
175+
'fields': {
176+
'VAR': {
177+
'name': INSTANCE_VARIABLE_NAME_PLACEHOLDER,
178+
}
179+
}
180+
}
181+
}
182+
}
170183
};
171184
exportedBlocks.push(callFunctionBlock);
172-
}
185+
});
173186

174187
const allVariables = workspace.getAllVariables();
175188
for (const variableModel of allVariables) {
@@ -199,32 +212,60 @@ export class ExtendedPythonGenerator extends PythonGenerator {
199212
'kind': 'block',
200213
'type': 'mrc_get_python_variable',
201214
'extraState': {
202-
'varKind': 'module',
203-
'moduleOrClassName': commonStorage.MODULE_NAME_PLACEHOLDER,
204-
'importModule': commonStorage.MODULE_NAME_PLACEHOLDER,
215+
'varKind': 'instance',
216+
'moduleOrClassName': FULL_CLASS_NAME_PLACEHOLDER,
217+
'importModule': '',
205218
'actualVariableName': actualVariableName,
219+
'selfLabel': INSTANCE_LABEL_PLACEHOLDER,
220+
'selfType': FULL_CLASS_NAME_PLACEHOLDER,
206221
'exportedVariable': true,
207222
},
208223
'fields': {
209-
'MODULE_OR_CLASS': commonStorage.MODULE_NAME_PLACEHOLDER,
224+
'MODULE_OR_CLASS': FULL_CLASS_NAME_PLACEHOLDER,
210225
'VAR': variableName,
211226
},
227+
'inputs': {
228+
'SELF': {
229+
'block': {
230+
'type': 'variables_get',
231+
'fields': {
232+
'VAR': {
233+
'name': INSTANCE_VARIABLE_NAME_PLACEHOLDER,
234+
}
235+
}
236+
}
237+
}
238+
}
212239
};
213240
exportedBlocks.push(getPythonModuleVariableBlock);
214241
const setPythonModuleVariableBlock = {
215242
'kind': 'block',
216243
'type': 'mrc_set_python_variable',
217244
'extraState': {
218-
'varKind': 'module',
219-
'moduleOrClassName': commonStorage.MODULE_NAME_PLACEHOLDER,
220-
'importModule': commonStorage.MODULE_NAME_PLACEHOLDER,
245+
'varKind': 'instance',
246+
'moduleOrClassName': FULL_CLASS_NAME_PLACEHOLDER,
247+
'importModule': '',
221248
'actualVariableName': actualVariableName,
249+
'selfLabel': INSTANCE_LABEL_PLACEHOLDER,
250+
'selfType': FULL_CLASS_NAME_PLACEHOLDER,
222251
'exportedVariable': true,
223252
},
224253
'fields': {
225-
'MODULE_OR_CLASS': commonStorage.MODULE_NAME_PLACEHOLDER,
254+
'MODULE_OR_CLASS': FULL_CLASS_NAME_PLACEHOLDER,
226255
'VAR': variableName,
227256
},
257+
'inputs': {
258+
'SELF': {
259+
'block': {
260+
'type': 'variables_get',
261+
'fields': {
262+
'VAR': {
263+
'name': INSTANCE_VARIABLE_NAME_PLACEHOLDER,
264+
}
265+
}
266+
}
267+
}
268+
}
228269
};
229270
exportedBlocks.push(setPythonModuleVariableBlock);
230271
}
@@ -234,3 +275,75 @@ export class ExtendedPythonGenerator extends PythonGenerator {
234275
}
235276

236277
export const extendedPythonGenerator = new ExtendedPythonGenerator();
278+
279+
export function replacePlaceholders(modulePath: string, exportedBlocks: Block[]) {
280+
const moduleName = commonStorage.getModuleName(modulePath);
281+
const className = commonStorage.moduleNameToClassName(moduleName);
282+
const fullClassName = moduleName + '.' + className;
283+
const instanceLabel = className.charAt(0).toLowerCase() + className.slice(1);
284+
const instanceVariableName = instanceLabel;
285+
286+
exportedBlocks.forEach((block) => {
287+
if (block.type === 'mrc_call_python_function') {
288+
if (block.extraState.args.length > 0 &&
289+
block.extraState.args[0].name === INSTANCE_LABEL_PLACEHOLDER) {
290+
block.extraState.args[0].name = instanceLabel;
291+
}
292+
if (block.extraState.args.length > 0 &&
293+
block.extraState.args[0].type === FULL_CLASS_NAME_PLACEHOLDER) {
294+
block.extraState.args[0].type = fullClassName;
295+
}
296+
if (block.fields.MODULE_OR_CLASS === FULL_CLASS_NAME_PLACEHOLDER) {
297+
block.fields.MODULE_OR_CLASS = fullClassName;
298+
}
299+
if (block.inputs.ARG0.block.type === 'variables_get' &&
300+
block.inputs.ARG0.block.fields.VAR.name === INSTANCE_VARIABLE_NAME_PLACEHOLDER) {
301+
block.inputs.ARG0.block.fields.VAR.name = instanceVariableName;
302+
}
303+
} else if (block.type === 'mrc_get_python_variable') {
304+
if (block.extraState.moduleOrClassName === FULL_CLASS_NAME_PLACEHOLDER) {
305+
block.extraState.moduleOrClassName = fullClassName;
306+
}
307+
if (block.extraState.selfLabel === INSTANCE_LABEL_PLACEHOLDER) {
308+
block.extraState.selfLabel = instanceLabel;
309+
}
310+
if (block.extraState.selfType === FULL_CLASS_NAME_PLACEHOLDER) {
311+
block.extraState.selfType = fullClassName;
312+
}
313+
if (block.fields.MODULE_OR_CLASS === FULL_CLASS_NAME_PLACEHOLDER) {
314+
block.fields.MODULE_OR_CLASS = fullClassName;
315+
}
316+
if (block.inputs.SELF.block.type === 'variables_get' &&
317+
block.inputs.SELF.block.fields.VAR.name === INSTANCE_VARIABLE_NAME_PLACEHOLDER) {
318+
block.inputs.SELF.block.fields.VAR.name = instanceVariableName;
319+
}
320+
} else if (block.type === 'mrc_set_python_variable') {
321+
if (block.extraState.moduleOrClassName === FULL_CLASS_NAME_PLACEHOLDER) {
322+
block.extraState.moduleOrClassName = fullClassName;
323+
}
324+
if (block.extraState.selfLabel === INSTANCE_LABEL_PLACEHOLDER) {
325+
block.extraState.selfLabel = instanceLabel;
326+
}
327+
if (block.extraState.selfType === FULL_CLASS_NAME_PLACEHOLDER) {
328+
block.extraState.selfType = fullClassName;
329+
}
330+
if (block.fields.MODULE_OR_CLASS === FULL_CLASS_NAME_PLACEHOLDER) {
331+
block.fields.MODULE_OR_CLASS = fullClassName;
332+
}
333+
if (block.inputs.SELF.block.type === 'variables_get' &&
334+
block.inputs.SELF.block.fields.VAR.name === INSTANCE_VARIABLE_NAME_PLACEHOLDER) {
335+
block.inputs.SELF.block.fields.VAR.name = instanceVariableName;
336+
}
337+
}
338+
});
339+
340+
// HeyLiz begin
341+
const HeyLiz = JSON.stringify(exportedBlocks);
342+
if (HeyLiz.indexOf(CLASS_NAME_PLACEHOLDER) !== -1 ||
343+
HeyLiz.indexOf(FULL_CLASS_NAME_PLACEHOLDER) !== -1 ||
344+
HeyLiz.indexOf(INSTANCE_LABEL_PLACEHOLDER) !== -1 ||
345+
HeyLiz.indexOf(INSTANCE_VARIABLE_NAME_PLACEHOLDER) !== -1) {
346+
throw new Error('HeyLiz - A placeholder didn\'t get replaced: ' + HeyLiz);
347+
}
348+
// HeyLiz end
349+
}

src/storage/common_storage.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import startingOpModeBlocks from '../modules/opmode_start.json';
2828
import startingMechanismBlocks from '../modules/mechanism_start.json';
2929
import startingRobotBlocks from '../modules/robot_start.json';
3030

31-
import {extendedPythonGenerator} from '../editor/extended_python_generator';
31+
import { extendedPythonGenerator, replacePlaceholders } from '../editor/extended_python_generator';
3232
import { createGeneratorContext } from '../editor/generator_context';
3333

3434
// Types, constants, and functions related to modules, regardless of where the modules are stored.
@@ -60,8 +60,6 @@ export const MODULE_TYPE_PROJECT = 'project';
6060
export const MODULE_TYPE_MECHANISM = 'mechanism';
6161
export const MODULE_TYPE_OPMODE = 'opmode';
6262

63-
export const MODULE_NAME_PLACEHOLDER = '%module_name%';
64-
6563
const DELIMITER_PREFIX = 'BlocksContent';
6664
const MARKER_BLOCKS_CONTENT = 'blocksContent: ';
6765
const MARKER_EXPORTED_BLOCKS = 'exportedBlocks: ';
@@ -392,22 +390,15 @@ export function extractBlocksContent(moduleContent: string): string {
392390
/**
393391
* Extract the exportedBlocks from the given module content.
394392
*/
395-
export function extractExportedBlocks(moduleName: string, moduleContent: string): Block[] {
393+
export function extractExportedBlocks(modulePath: string, moduleContent: string): Block[] {
396394
const parts = getParts(moduleContent);
397395
let exportedBlocksContent = parts[PARTS_INDEX_EXPORTED_BLOCKS];
398396
if (exportedBlocksContent.startsWith(MARKER_EXPORTED_BLOCKS)) {
399397
exportedBlocksContent = exportedBlocksContent.substring(MARKER_EXPORTED_BLOCKS.length);
400398
}
401399

402400
const exportedBlocks: Block[] = JSON.parse(exportedBlocksContent);
403-
exportedBlocks.forEach((block) => {
404-
if (block.extraState?.importModule === MODULE_NAME_PLACEHOLDER) {
405-
block.extraState.importModule = moduleName;
406-
}
407-
if (block.fields?.MODULE_OR_CLASS === MODULE_NAME_PLACEHOLDER) {
408-
block.fields.MODULE_OR_CLASS = moduleName;
409-
}
410-
});
401+
replacePlaceholders(modulePath, exportedBlocks);
411402
return exportedBlocks;
412403
}
413404

src/toolbox/methods_category.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class MethodsCategory {
6060
// Collect the method names for mrc_class_method_def blocks that are
6161
// already in the blockly workspace.
6262
const methodNamesAlreadyUsed: string[] = [];
63-
workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => {
63+
workspace.getBlocksByType('mrc_class_method_def').forEach((block) => {
6464
const classMethodDefBlock = block as ClassMethodDefBlock;
6565
if (!classMethodDefBlock.mrcCanChangeSignature) {
6666
methodNamesAlreadyUsed.push(classMethodDefBlock.getFieldValue('NAME'));
@@ -108,7 +108,7 @@ export class MethodsCategory {
108108
// For each mrc_class_method_def block in the blockly workspace, check if it
109109
// can be called from within the class, and if so, add a
110110
// mrc_call_python_function block.
111-
workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => {
111+
workspace.getBlocksByType('mrc_class_method_def').forEach((block) => {
112112
const classMethodDefBlock = block as ClassMethodDefBlock;
113113
if (classMethodDefBlock.mrcCanBeCalledWithinClass) {
114114
const callPythonFunctionBlock: toolboxItems.Block = {

0 commit comments

Comments
 (0)