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
73 changes: 50 additions & 23 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Button,
ConfigProvider,
Flex,
Input,
message,
Popconfirm,
Space,
Expand All @@ -16,7 +15,6 @@ import {
theme,
Upload
} from 'antd';
import type { InputRef } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
import type { UploadProps } from 'antd';
import {
Expand Down Expand Up @@ -55,8 +53,6 @@ import * as editor from './editor/editor';
import { extendedPythonGenerator } from './editor/extended_python_generator';
import { createGeneratorContext, GeneratorContext } from './editor/generator_context';

import * as toolboxItems from './toolbox/items';
import * as toolbox from './toolbox/toolbox';
//import { testAllBlocksInToolbox } from './toolbox/toolbox_tests';
import ToolboxSettingsModal from './toolbox/settings';

Expand Down Expand Up @@ -173,6 +169,9 @@ const App: React.FC = () => {
}, [storage]);

const fetchMostRecentModulePath = async () => {
if (!storage) {
return;
}
try {
const value = await storage.fetchEntry('mostRecentModulePath', '');
setMostRecentModulePath(value);
Expand All @@ -183,6 +182,9 @@ const App: React.FC = () => {
};

const initializeShownPythonToolboxCategories = async () => {
if (!storage) {
return;
}
try {
const value = await storage.fetchEntry('shownPythonToolboxCategories', '[]');
const shownCategories: string[] = JSON.parse(value);
Expand All @@ -206,6 +208,10 @@ const App: React.FC = () => {

const fetchListOfModules = async (): Promise<commonStorage.Project[]> => {
return new Promise(async (resolve, reject) => {
if (!storage) {
reject(new Error());
return;
}
try {
const array = await storage.listModules();
setModules(array)
Expand Down Expand Up @@ -348,7 +354,7 @@ const App: React.FC = () => {
}
if (currentModule && blocklyComponent.current && generatorContext.current) {
const blocklyWorkspace = blocklyComponent.current.getBlocklyWorkspace();
setGeneratedCode(extendedPythonGenerator.workspaceToCode(
setGeneratedCode(extendedPythonGenerator.mrcWorkspaceToCode(
blocklyWorkspace, generatorContext.current));
} else {
setGeneratedCode('');
Expand Down Expand Up @@ -462,6 +468,9 @@ const App: React.FC = () => {
};

const handleNewProjectNameOk = async (newProjectClassName: string) => {
if (!storage || !currentModule) {
return;
}
const newProjectName = commonStorage.classNameToModuleName(newProjectClassName);
const newProjectPath = commonStorage.makeProjectPath(newProjectName);
if (newProjectNameModalPurpose === PURPOSE_NEW_PROJECT) {
Expand Down Expand Up @@ -553,7 +562,10 @@ const App: React.FC = () => {
};

const handleNewModuleNameOk = async (newModuleClassName: string) => {
const newModuleName = commonStorage.classNameToModuleName(newModuleClassName);
if (!storage || !currentModule) {
return;
}
const newModuleName = commonStorage.classNameToModuleName(newModuleClassName);
const newModulePath = commonStorage.makeModulePath(currentModule.projectName, newModuleName);
if (newModuleNameModalPurpose === PURPOSE_NEW_MECHANISM) {
const mechanismContent = commonStorage.newMechanismContent(
Expand Down Expand Up @@ -612,26 +624,30 @@ const App: React.FC = () => {
};

const handleSaveClicked = async () => {
saveBlocks((success) => {});
saveBlocks();
};

const saveBlocks = async (): boolean => {
if (blocksEditor.current && currentModulePath) {
const saveBlocks = async (): Promise<boolean> => {
return new Promise(async (resolve, reject) => {
if (!blocksEditor.current || !currentModulePath) {
reject(new Error());
return;
}
try {
await blocksEditor.current.saveBlocks();
messageApi.open({
type: 'success',
content: 'Save completed successfully.',
});
return true;
} catch (e: Error) {
resolve(true);
} catch (e) {
console.log('Failed to save the blocks. Caught the following error...');
console.log(e);
setAlertErrorMessage('Failed to save the blocks.');
setAlertErrorVisible(true);
reject(new Error('Failed to save the blocks.'));
}
}
return false;
});
};

const handleRenameClicked = () => {
Expand Down Expand Up @@ -715,7 +731,7 @@ const App: React.FC = () => {
afterPopconfirmOk.current = () => {
setOpenPopconfirm(false);
checkIfBlocksWereModified(async () => {
if (!currentModule) {
if (!storage || !currentModule) {
return;
}
if (currentModule.moduleType == commonStorage.MODULE_TYPE_PROJECT) {
Expand Down Expand Up @@ -779,40 +795,48 @@ const App: React.FC = () => {
}
return isBlocks || Upload.LIST_IGNORE;
},
onChange: (info) => {
},
customRequest: ({ file, onSuccess, onError }) => {
if (!onSuccess || !onError) {
return;
}
const fileObject = file as File;
const reader = new FileReader();
reader.onload = async (event) => {
const dataUrl = event.target.result;
const uploadProjectName = commonStorage.makeUploadProjectName(file.name, getProjectNames());
const dataUrl = event.target?.result;
if (!storage || !dataUrl) {
return;
}
const uploadProjectName = commonStorage.makeUploadProjectName(fileObject.name, getProjectNames());
try {
await storage.uploadProject(uploadProjectName, dataUrl);
await storage.uploadProject(uploadProjectName, dataUrl as string);
onSuccess('Upload successful');
await fetchListOfModules();
const uploadProjectPath = commonStorage.makeProjectPath(uploadProjectName);
setCurrentModulePath(uploadProjectPath);
} catch (e) {
console.log('Failed to upload the project. Caught the following error...');
console.log(e);
onError('Failed to upload the project.');
onError(new Error('Failed to upload the project.'));
setAlertErrorMessage('Failed to upload the project');
setAlertErrorVisible(true);
}
};
reader.onerror = (error) => {
console.log('Failed to upload the project. reader.onerror called with the following error...');
console.log(error);
onError('Failed to upload the project.');
onError(new Error('Failed to upload the project.'));
setAlertErrorMessage('Failed to upload the project');
setAlertErrorVisible(true);
};
reader.readAsDataURL(file);
reader.readAsDataURL(fileObject);
},
};

const handleDownloadClicked = () => {
checkIfBlocksWereModified(async () => {
if (!storage || !currentModule) {
return;
}
try {
const url = await storage.downloadProject(currentModule.projectName);
const link = document.createElement('a');
Expand All @@ -833,13 +857,16 @@ const App: React.FC = () => {
};

const handleToolboxSettingsOk = async (updatedShownCategories: Set<string>) => {
if (!storage) {
return;
}
setShownPythonToolboxCategories(updatedShownCategories);
const array = Array.from(updatedShownCategories);
array.sort();
storage.saveEntry('shownPythonToolboxCategories', JSON.stringify(array));
};

const handleModuleSelected: TreeProps['onSelect'] = (a: React.Key[], e) => {
const handleModuleSelected: TreeProps['onSelect'] = (a: React.Key[]) => {
if (a.length === 1) {
checkIfBlocksWereModified(() => {
setTreeSelectedKey(a[0]);
Expand Down
13 changes: 6 additions & 7 deletions src/blocks/mrc_call_python_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


import * as Blockly from 'blockly';
import { Order, PythonGenerator } from 'blockly/python';
import { Order } from 'blockly/python';

import * as pythonUtils from './utils/generated/python';
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
Expand Down Expand Up @@ -58,7 +58,7 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType {
mrcImportModule: string,
mrcActualFunctionName: string,
mrcExportedFunction: boolean,
renameMethod(this: CallPythonFunctionBlock, oldName: string, newName: string): void;
renameMethod(this: CallPythonFunctionBlock, newName: string): void;
mutateMethod(this: CallPythonFunctionBlock, defBlockExtraState: ClassMethodDefExtraState): void;
}
type CallPythonFunctionMixinType = typeof CALL_PYTHON_FUNCTION;
Expand Down Expand Up @@ -139,7 +139,7 @@ const CALL_PYTHON_FUNCTION = {
break;
}
default:
throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind)
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
}
const funcTooltip = this.mrcTooltip;
if (funcTooltip) {
Expand Down Expand Up @@ -261,7 +261,7 @@ const CALL_PYTHON_FUNCTION = {
break;
}
default:
throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind)
throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind)
}
}

Expand Down Expand Up @@ -305,7 +305,7 @@ const CALL_PYTHON_FUNCTION = {
defBlockExtraState.params.forEach((param) => {
this.mrcArgs.push({
'name': param.name,
'type': param.type,
'type': param.type ?? '',
});
});
this.updateBlock_();
Expand Down Expand Up @@ -345,7 +345,6 @@ export const pythonFromBlock = function(
break;
}
case FunctionKind.CONSTRUCTOR: {
const callPythonFunctionBlock = block as CallPythonFunctionBlock;
const className = block.getFieldValue(pythonUtils.FIELD_MODULE_OR_CLASS_NAME);
code = className;
break;
Expand All @@ -367,7 +366,7 @@ export const pythonFromBlock = function(
break;
}
default:
throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind)
throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind)
}
code += '(' + generateCodeForArguments(callPythonFunctionBlock, generator, argStartIndex) + ')';
if (block.outputConnection) {
Expand Down
37 changes: 15 additions & 22 deletions src/blocks/mrc_class_method_def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,12 @@ export const BLOCK_NAME = 'mrc_class_method_def';
const MUTATOR_BLOCK_NAME = 'methods_mutatorarg';
const PARAM_CONTAINER_BLOCK_NAME = 'method_param_container';

class MethodInput extends Blockly.inputs.Input {
readonly type = Blockly.inputs.inputTypes.CUSTOM;
constructor(name: string, block: Blockly.Block) {
super(name, block);
}
}

export type Parameter = {
name: string,
type?: string,
};

type ClassMethodDefBlock = Blockly.Block & ClassMethodDefMixin & Blockly.BlockSvg;
export type ClassMethodDefBlock = Blockly.Block & ClassMethodDefMixin & Blockly.BlockSvg;
interface ClassMethodDefMixin extends ClassMethodDefMixinType {
mrcCanChangeSignature: boolean,
mrcCanBeCalledWithinClass: boolean,
Expand All @@ -57,7 +50,7 @@ interface ClassMethodDefMixin extends ClassMethodDefMixinType {
type ClassMethodDefMixinType = typeof CLASS_METHOD_DEF;

/** Extra state for serialising call_python_* blocks. */
type ClassMethodDefExtraState = {
export type ClassMethodDefExtraState = {
/**
* Can change name and parameters and return type
*/
Expand Down Expand Up @@ -178,7 +171,7 @@ const CLASS_METHOD_DEF = {

let paramBlock = containerBlock.getInputTargetBlock('STACK');
while (paramBlock && !paramBlock.isInsertionMarker()) {
let param : Parameter = {
const param : Parameter = {
name : paramBlock.getFieldValue('NAME'),
type : ''
}
Expand All @@ -192,13 +185,13 @@ const CLASS_METHOD_DEF = {
decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) {
// This is a special sub-block that only gets created in the mutator UI.
// It acts as our "top block"
let topBlock = workspace.newBlock(PARAM_CONTAINER_BLOCK_NAME);
const topBlock = workspace.newBlock(PARAM_CONTAINER_BLOCK_NAME);
(topBlock as Blockly.BlockSvg).initSvg();

// Then we add one sub-block for each item in the list.
var connection = topBlock!.getInput('STACK')!.connection;
let connection = topBlock!.getInput('STACK')!.connection;

for (var i = 0; i < this.mrcParameters.length; i++) {
for (let i = 0; i < this.mrcParameters.length; i++) {
let itemBlock = workspace.newBlock(MUTATOR_BLOCK_NAME);
(itemBlock as Blockly.BlockSvg).initSvg();
itemBlock.setFieldValue(this.mrcParameters[i].name, 'NAME')
Expand Down Expand Up @@ -237,7 +230,7 @@ const CLASS_METHOD_DEF = {

const legalName = findLegalMethodName(name, this);
const oldName = nameField.getValue();
if (oldName !== name && oldName !== legalName) {
if (oldName && oldName !== name && oldName !== legalName) {
// Rename any callers.
renameMethodCallers(this.workspace, oldName, legalName);
}
Expand Down Expand Up @@ -282,7 +275,7 @@ function findLegalMethodName(name: string, block: ClassMethodDefBlock): string {
* @returns True if the name is used, otherwise return false.
*/
function isMethodNameUsed(
name: string, workspace: Workspace, opt_exclude?: Block): boolean {
name: string, workspace: Blockly.Workspace, opt_exclude?: Blockly.Block): boolean {
const nameLowerCase = name.toLowerCase();
for (const block of workspace.getBlocksByType('mrc_class_method_def')) {
if (block === opt_exclude) {
Expand All @@ -291,8 +284,9 @@ function isMethodNameUsed(
if (nameLowerCase === block.getFieldValue('NAME').toLowerCase()) {
return true;
}
if (block.mrcPythonMethodName &&
nameLowerCase === block.mrcPythonMethodName.toLowerCase()) {
const classMethodDefBlock = block as ClassMethodDefBlock;
if (classMethodDefBlock.mrcPythonMethodName &&
nameLowerCase === classMethodDefBlock.mrcPythonMethodName.toLowerCase()) {
return true;
}
}
Expand All @@ -315,11 +309,10 @@ interface MethodMutatorArgMixin extends MethodMutatorArgMixinType{
type MethodMutatorArgMixinType = typeof METHODS_MUTATORARG;

function setName(block: Blockly.BlockSvg){
let parentBlock = ChangeFramework.getParentOfType(block, PARAM_CONTAINER_BLOCK_NAME);
const parentBlock = ChangeFramework.getParentOfType(block, PARAM_CONTAINER_BLOCK_NAME);
if (parentBlock) {
let done = false;
let variableBlocks = parentBlock!.getDescendants(true)
let otherNames: string[] = []
const variableBlocks = parentBlock!.getDescendants(true)
const otherNames: string[] = []
variableBlocks?.forEach(function (variableBlock) {
if (variableBlock != block) {
otherNames.push(variableBlock.getFieldValue('NAME'));
Expand Down Expand Up @@ -465,7 +458,7 @@ export const pythonFromBlock = function (
xfix2 = xfix1;
}
if(block.mrcPythonMethodName == '__init__'){
branch = generator.defineClassVariables(block.workspace) + branch;
branch = generator.defineClassVariables() + branch;
}
if (returnValue) {
returnValue = generator.INDENT + 'return ' + returnValue + '\n';
Expand Down
2 changes: 1 addition & 1 deletion src/blocks/mrc_get_python_enum_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


import * as Blockly from 'blockly';
import { Order, PythonGenerator } from 'blockly/python';
import { Order } from 'blockly/python';

import * as pythonUtils from './utils/generated/python';
import { createFieldDropdown } from '../fields/FieldDropdown';
Expand Down
2 changes: 1 addition & 1 deletion src/blocks/mrc_get_python_variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


import * as Blockly from 'blockly';
import { Order, PythonGenerator } from 'blockly/python';
import { Order } from 'blockly/python';

import * as pythonUtils from './utils/generated/python';
import { createFieldDropdown } from '../fields/FieldDropdown';
Expand Down
Loading