Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
99b37b8
Added support for classes, class instances and methods with a configu…
blauwefant Jun 14, 2025
e109ac2
Add a missing hook needed for further customization.
blauwefant Jun 14, 2025
5a297a3
Color fix for turtle forward (remove test code)
blauwefant Jun 26, 2025
96a789b
Minor fix for de-scrubbing lines
blauwefant Jul 1, 2025
6317c4f
Minor fix for de-scrubbing default values
blauwefant Jul 15, 2025
37501b0
Minor fix: include imports as scrubbed lines, which is important in c…
blauwefant Jul 17, 2025
53a9f9f
Extracted library metadata into separate JSON files.
blauwefant Aug 10, 2025
be1a3d6
Add missing handling of custom library metadata field (attaching a cu…
blauwefant Aug 11, 2025
1e9837d
Minor cleanups
blauwefant Aug 12, 2025
f540a37
Upgrade Blockly to 12.3.0
blauwefant Aug 31, 2025
238f812
Upgrade Blockly to 12.3.0
blauwefant Aug 31, 2025
ffc5485
Argument parsing fix + Jest unit test
blauwefant Sep 1, 2025
07b1cee
Import fix for constructors.
blauwefant Sep 1, 2025
22be7ed
Improved implementation for instance attributes.
blauwefant Sep 1, 2025
a7726b5
Improved implementation for instance attributes.
blauwefant Sep 1, 2025
ce8c7c4
Various fixes for resolving toolbar content.
blauwefant Sep 2, 2025
55de05d
Support for attribute aliases, for enum-like classes.
blauwefant Sep 4, 2025
d6c42c7
Support for argument aliases, such as mutually exclusive keyword argu…
blauwefant Sep 7, 2025
eca9fc6
Minor improvements.
blauwefant Sep 7, 2025
0ee8218
Added support for type aliases.
blauwefant Sep 18, 2025
8e5baca
Cleaned up support for custom fields.
blauwefant Sep 21, 2025
f0c17de
Some minor fixes for resolving variables from a library
blauwefant Sep 23, 2025
d711d9e
Apply library colours to attributes.
blauwefant Sep 25, 2025
321d313
Clean up ast_Call to not place the message together with the first ar…
blauwefant Sep 25, 2025
35b69be
Fixed resolving an attribute directly on a function result.
blauwefant Sep 25, 2025
679243f
Added support for a separator in the toolboxes.
blauwefant Sep 28, 2025
2c870e0
Cleaned up handling op top level statements.
blauwefant Sep 28, 2025
ee72312
Cleaned up and fixed handling of parentheses.
blauwefant Sep 29, 2025
4daf43c
Minor toolbox fix
blauwefant Sep 29, 2025
4b0909b
Reduce overhead of re-creating the toolbox.
blauwefant Oct 1, 2025
56f35d9
Fix shape of regular attributes.
blauwefant Oct 2, 2025
62e017e
Minor turtle library fixes.
blauwefant Oct 2, 2025
8259cca
Added eslint and some code fixes.
blauwefant Oct 3, 2025
d8ca4b6
Minor fixes to previous check in.
blauwefant Oct 4, 2025
3cb2a94
Minor cleanup.
blauwefant Oct 4, 2025
33c68db
Simplified blocks for negative numbers.
blauwefant Oct 5, 2025
9f94e02
Added a colour conversion configuration, to allow changing colours us…
blauwefant Oct 8, 2025
eb22ccd
Added support for labels on attributes.
blauwefant Oct 24, 2025
31048f6
Minor resolving fix.
blauwefant Oct 24, 2025
0c99121
Cleaning up messages
blauwefant Oct 25, 2025
66aa226
Work on translations.
blauwefant Oct 25, 2025
0e69fd7
Translations for attributes and names.
blauwefant Oct 26, 2025
fa70eee
Minor translation related improvements
blauwefant Oct 28, 2025
13f62db
Various fixes.
blauwefant Oct 30, 2025
a708a05
Translation of keyword parameters.
blauwefant Nov 3, 2025
9a4b461
Also allow -> in function type hint.
blauwefant Nov 11, 2025
d1c1f7b
Some minor turtle library additions
blauwefant Nov 13, 2025
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
5,643 changes: 4,001 additions & 1,642 deletions dist/block_mirror.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/block_mirror_unbundled.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/skulpt_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5784,7 +5784,7 @@ function astForArguments(c, n) {

/* This function handles both typedargslist (function definition)
and varargslist (lambda definition).
parameters: '(' [typedargslist] ')'
parameters: '(' [typedargslist] ')'
typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' [
'*' [tfpdef] (',' tfpdef ['=' test])* [',' ['**' tfpdef [',']]]
| '**' tfpdef [',']]]
Expand All @@ -5798,7 +5798,7 @@ function astForArguments(c, n) {
| '**' vfpdef [',']
)
vfpdef: NAME
*/
*/
if (n.type === SYM.parameters) {
if (NCH(n) === 2) {
// () as arglist
Expand Down
28 changes: 28 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig } from "eslint/config";
import js from "@eslint/js";
import globals from "globals";

export default defineConfig([
js.configs.recommended,
{
languageOptions: {
globals: globals.browser,
},
},
{ files: ["src/**/*.js"] },
{
ignores: [
"webpack.config*",
"dist/**/*.js",
"lib/**/*.js",
"src/skulpt/*.js",
],
},
{
rules: {
"no-undef": "off", // Currently not yet possible
"no-unused-vars": "off", // TODO enable in the future
"no-cond-assign": "off", // TODO should be enabled, few instances remaining
},
},
]);
135 changes: 135 additions & 0 deletions export_lib_translations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const path = require('path');
const fs = require('fs')

python = {}
python.pythonGenerator = {}
python.pythonGenerator.blank = '___'
const blockMirrorLibraries = require('./src/libraries.js');

let baseDir = path.resolve(process.argv.length > 2 ? process.argv[2] : __dirname)
let librariesDir = path.resolve(baseDir, 'src/libraries/')
let librariesConfig = {}

function addMemberToTranslationsMap(member, translationsMap) {
if (member instanceof blockMirrorLibraries.PythonFunction && !member.isAliasOf) {
let key, value, premessage;

if (member.premessage && (!member.pythonClass || member.premessage !== member.pythonClass.name)) {
premessage = member.premessage.trimEnd() + ' '
} else {
premessage = "";
}

if (member instanceof blockMirrorLibraries.PythonConstructorMethod) {
key = member.pythonClass.fullName;
value = `${premessage}${member.message}${member.label}${member.postmessage}`;
} else {
key = member.fullName;

if (member.labels.length > 1) {
labelsPart = "{" + member.labels.join(" | ") + "}";
} else {
labelsPart = member.labels[0];
}

if (member instanceof blockMirrorLibraries.PythonMethod) {
value = `${premessage}{}${member.message}${labelsPart}${member.postmessage}`;
} else if (member.pythonModule.fullName === "") {
value = `${member.premessage}${member.message}${labelsPart}${member.postmessage}`;
} else {
value = `${premessage}{}${member.message}${labelsPart}${member.postmessage}`;
}
}
translationsMap.set(key, value);

if (!member.name.startsWith("__") || member.name === "__init__") {
for (let parameter of member.parameters) {
if (
parameter.keyword &&
(member.classmethod ? parameter.name !== "cls" : parameter.name !== "self") &&
parameter.name.length > 1 &&
!parameter.variableLength
) {
let key = "KEYWORD:" + parameter.name;
let value = translationsMap.get(key) ?? new Set();
value.add(member.fullName);
translationsMap.set(key, value);
}
}
}
} else if (member instanceof blockMirrorLibraries.PythonAttribute && !member.isAliasOf) {
let premessage, value, labelsPart;

if (member.labels.length > 1) {
labelsPart = "{" + member.labels.join(" | ") + "}";
} else {
labelsPart = member.labels[0];
}

if (member.premessage && (!member.pythonClass || member.premessage !== member.pythonClass.name)) {
premessage = member.premessage + " ";
} else {
premessage = "";
}

if (!!member.pythonClass || member.pythonModule.fullName !== "") {
value = `${premessage}{}${member.message}${labelsPart}${member.postmessage}`;
translationsMap.set(member.fullName, value);
}
}
}

fs.readdirSync(librariesDir, {recursive: true}).forEach(file => {
if (file.endsWith(".json")) {
let libraryName = file.substring(0, file.length -5).replace('/', ' ');
let libraryValue = fs.readFileSync(path.resolve(librariesDir, file), 'utf8')
librariesConfig[libraryName] = JSON.parse(libraryValue)
}
});

let libraries = new blockMirrorLibraries.Libraries(librariesConfig)
let libraryTranslationsMap = new Map()

for (const library of libraries.values()) {
let libraryPrefix = library.name.split(" ", 1)[0]
let translationsMap = libraryTranslationsMap.get(libraryPrefix) ?? new Map()

for (const module of library.modules.values()) {
for (const member of module.members.values()) {
if (member instanceof blockMirrorLibraries.PythonClass) {
for (const member2 of member.members.values()) {
addMemberToTranslationsMap(member2, translationsMap);
}
} else {
addMemberToTranslationsMap(member, translationsMap);
}
}
}

libraryTranslationsMap.set(libraryPrefix, translationsMap)
}

for (let [libraryName, translationsMap] of libraryTranslationsMap) {
translationsMap = new Map([...translationsMap.entries()].sort());
let potData = "# BlockMirror library " + libraryName + " gettext portable object template\n"
potData += "# Default English texts are part of the library definitions.\n\n"

for (const [key, value] of translationsMap) {
let isKeyword = key.startsWith("KEYWORD:")
let cleanKey = isKeyword ? key.substring(8) : key;

if (/[a-zA-Z]/.test(cleanKey)) {
if (isKeyword) {
potData += `#. keyword parameter from: ${[...value].sort().join(', ')}\n`;
} else if (value && value !== cleanKey) {
potData += `#. ${value}\n`;
}
potData += `msgid "${cleanKey.replace('"', '\\"')}"\n`;
potData += `msgstr ""\n\n`;
}
}

let potDir = path.resolve(baseDir, 'translations/libraries/' + libraryName)
fs.mkdirSync(potDir, {recursive: true})
fs.writeFileSync(path.resolve(potDir, 'template.pot'), potData)
}
Loading