pxt-core 7.5.1 → 7.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/cli.js +57 -5
- package/built/pxt.js +194 -11
- package/built/pxtblockly.js +714 -657
- package/built/pxtblocks.d.ts +7 -0
- package/built/pxtblocks.js +159 -40
- package/built/pxtcompiler.js +42 -4
- package/built/pxtlib.d.ts +2 -0
- package/built/pxtlib.js +22 -0
- package/built/pxtpy.js +73 -2
- package/built/server.js +4 -0
- package/built/target.js +1 -1
- package/built/web/authcode/css/main.1cf9dc37.css +2 -0
- package/built/web/authcode/js/main.03da4c20.js +2 -0
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtasseteditor.js +1 -1
- package/built/web/pxtblockly.js +2 -2
- package/built/web/pxtblocks.js +1 -1
- package/built/web/pxtcompiler.js +1 -1
- package/built/web/pxtembed.js +2 -2
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtpy.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/built/web/react-common-authcode.css +6008 -0
- package/built/web/react-common-skillmap.css +1 -1
- package/built/web/rtlreact-common-skillmap.css +1 -1
- package/built/web/rtlsemantic.css +1 -1
- package/built/web/semantic.css +1 -1
- package/built/web/skillmap/css/{main.4939cd1e.chunk.css → main.e0620cee.chunk.css} +1 -1
- package/built/web/skillmap/js/main.f6866fc6.chunk.js +1 -0
- package/common-docs/faq.md +1 -1
- package/common-docs/translate.md +2 -2
- package/localtypings/pxtblockly.d.ts +1 -0
- package/package.json +5 -3
- package/pxtarget.json +1 -1
- package/react-common/components/controls/EditorToggle.tsx +109 -0
- package/react-common/components/controls/Modal.tsx +1 -1
- package/react-common/components/profile/UserPane.tsx +8 -4
- package/react-common/styles/controls/Button.less +30 -14
- package/react-common/styles/controls/EditorToggle.less +233 -0
- package/react-common/styles/controls/MenuDropdown.less +1 -1
- package/react-common/styles/profile/profile.less +2 -2
- package/react-common/styles/react-common-authcode-core.less +10 -0
- package/react-common/styles/react-common-authcode.less +12 -0
- package/react-common/styles/react-common-variables.less +26 -2
- package/react-common/styles/react-common.less +1 -0
- package/theme/common.less +5 -0
- package/theme/image-editor/imageEditor.less +7 -116
- package/theme/tutorial-sidebar.less +11 -4
- package/webapp/public/authcode.html +1 -0
- package/webapp/public/blockly/blockly_compressed.js +555 -617
- package/webapp/public/index.html +1 -1
- package/webapp/public/skillmap.html +2 -2
- package/built/web/skillmap/js/main.2a4cb15b.chunk.js +0 -1
package/built/pxtblocks.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ declare namespace pxt.blocks {
|
|
|
39
39
|
alreadyDeclared?: BlockDeclarationType;
|
|
40
40
|
firstReference?: Blockly.Block;
|
|
41
41
|
isAssigned?: boolean;
|
|
42
|
+
isFunctionParameter?: boolean;
|
|
42
43
|
}
|
|
43
44
|
function compileExpression(e: Environment, b: Blockly.Block, comments: string[]): JsNode;
|
|
44
45
|
interface Environment {
|
|
@@ -264,6 +265,12 @@ declare namespace pxt.blocks {
|
|
|
264
265
|
protected createDom_(): void;
|
|
265
266
|
protected highlightSearchGroup_(blocks: Blockly.BlockSvg[]): void;
|
|
266
267
|
protected unhighlightSearchGroup_(blocks: Blockly.BlockSvg[]): void;
|
|
268
|
+
/**
|
|
269
|
+
* https://github.com/google/blockly-samples/blob/master/plugins/workspace-search/src/WorkspaceSearch.js#L633
|
|
270
|
+
*
|
|
271
|
+
* Modified to center offscreen blocks.
|
|
272
|
+
*/
|
|
273
|
+
protected scrollToVisible_(block: Blockly.BlockSvg): void;
|
|
267
274
|
open(): void;
|
|
268
275
|
close(): void;
|
|
269
276
|
}
|
package/built/pxtblocks.js
CHANGED
|
@@ -179,7 +179,7 @@ var pxt;
|
|
|
179
179
|
}
|
|
180
180
|
else if (b.type == "argument_reporter_array") {
|
|
181
181
|
if (!tp) {
|
|
182
|
-
tp =
|
|
182
|
+
tp = lookup(e, b, b.getFieldValue("VALUE")).type;
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
if (tp)
|
|
@@ -492,8 +492,14 @@ var pxt;
|
|
|
492
492
|
// Last pass: if some variable has no type (because it was never used or
|
|
493
493
|
// assigned to), just unify it with int...
|
|
494
494
|
e.allVariables.forEach((v) => {
|
|
495
|
-
if (getConcreteType(v.type).type == null)
|
|
496
|
-
|
|
495
|
+
if (getConcreteType(v.type).type == null) {
|
|
496
|
+
if (!v.isFunctionParameter) {
|
|
497
|
+
union(v.type, ground(v.type.isArrayType ? "number[]" : pNumber.type));
|
|
498
|
+
}
|
|
499
|
+
else if (v.type.isArrayType) {
|
|
500
|
+
v.type.type = "any[]";
|
|
501
|
+
}
|
|
502
|
+
}
|
|
497
503
|
});
|
|
498
504
|
function connectionCheck(i) {
|
|
499
505
|
return i.name ? i.connection && i.connection.check_ && i.connection.check_.length ? i.connection.check_[0] : "T" : undefined;
|
|
@@ -582,9 +588,31 @@ var pxt;
|
|
|
582
588
|
function compileNumber(e, b, comments) {
|
|
583
589
|
return blocks_1.H.mkNumberLiteral(extractNumber(b));
|
|
584
590
|
}
|
|
591
|
+
function isNumericLiteral(e, b) {
|
|
592
|
+
if (!b)
|
|
593
|
+
return false;
|
|
594
|
+
if (b.type === "math_number" || b.type === "math_integer" || b.type === "math_number_minmax" || b.type === "math_whole_number") {
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
const blockInfo = e.stdCallTable[b.type];
|
|
598
|
+
if (!blockInfo)
|
|
599
|
+
return false;
|
|
600
|
+
const { comp } = blockInfo;
|
|
601
|
+
if (blockInfo.attrs.shim === "TD_ID" && comp.parameters.length === 1) {
|
|
602
|
+
const fieldValue = b.getFieldValue(comp.parameters[0].definitionName);
|
|
603
|
+
if (fieldValue) {
|
|
604
|
+
return !isNaN(parseInt(fieldValue));
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
return isNumericLiteral(e, getInputTargetBlock(b, comp.parameters[0].definitionName));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
function isLiteral(e, b) {
|
|
613
|
+
return isNumericLiteral(e, b) || b.type === "logic_boolean" || b.type === "text";
|
|
614
|
+
}
|
|
585
615
|
let opToTok = {
|
|
586
|
-
// POWER gets a special treatment because there's no operator for it in
|
|
587
|
-
// TouchDevelop
|
|
588
616
|
"ADD": "+",
|
|
589
617
|
"MINUS": "-",
|
|
590
618
|
"MULTIPLY": "*",
|
|
@@ -599,11 +627,21 @@ var pxt;
|
|
|
599
627
|
"NEQ": "!=",
|
|
600
628
|
"POWER": "**"
|
|
601
629
|
};
|
|
630
|
+
function isComparisonOp(op) {
|
|
631
|
+
return ["LT", "LTE", "GT", "GTE", "EQ", "NEQ"].indexOf(op) !== -1;
|
|
632
|
+
}
|
|
602
633
|
function compileArithmetic(e, b, comments) {
|
|
603
634
|
let bOp = b.getFieldValue("OP");
|
|
604
635
|
let left = getInputTargetBlock(b, "A");
|
|
605
636
|
let right = getInputTargetBlock(b, "B");
|
|
606
637
|
let args = [compileExpression(e, left, comments), compileExpression(e, right, comments)];
|
|
638
|
+
// Special handling for the case of comparing two literals (e.g. 0 === 5). TypeScript
|
|
639
|
+
// throws an error if we don't first cast to any
|
|
640
|
+
if (isComparisonOp(bOp) && isLiteral(e, left) && isLiteral(e, right)) {
|
|
641
|
+
if (blocks_1.flattenNode([args[0]]).output !== blocks_1.flattenNode([args[1]]).output) {
|
|
642
|
+
args = args.map(arg => blocks_1.H.mkParenthesizedExpression(blocks_1.mkGroup([arg, blocks_1.mkText(" as any")])));
|
|
643
|
+
}
|
|
644
|
+
}
|
|
607
645
|
let t = returnType(e, left).type;
|
|
608
646
|
if (t == pString.type) {
|
|
609
647
|
if (bOp == "EQ")
|
|
@@ -711,7 +749,10 @@ var pxt;
|
|
|
711
749
|
const stmts = getInputTargetBlock(b, "STACK");
|
|
712
750
|
const argsDeclaration = b.getArguments().map(a => {
|
|
713
751
|
if (a.type == "Array") {
|
|
714
|
-
|
|
752
|
+
const binding = lookup(e, b, a.name);
|
|
753
|
+
const declaredType = getConcreteType(binding.type);
|
|
754
|
+
const paramType = ((declaredType === null || declaredType === void 0 ? void 0 : declaredType.type) && declaredType.type !== "Array") ? declaredType.type : "any[]";
|
|
755
|
+
return `${escapeVarName(a.name, e)}: ${paramType}`;
|
|
715
756
|
}
|
|
716
757
|
return `${escapeVarName(a.name, e)}: ${a.type}`;
|
|
717
758
|
});
|
|
@@ -1859,7 +1900,7 @@ var pxt;
|
|
|
1859
1900
|
return res;
|
|
1860
1901
|
}
|
|
1861
1902
|
function getEscapedCBParameters(b, stdfun, e) {
|
|
1862
|
-
return getCBParameters(b, stdfun).map(binding => lookup(e, b, binding
|
|
1903
|
+
return getCBParameters(b, stdfun).map(binding => lookup(e, b, binding.name).escapedName);
|
|
1863
1904
|
}
|
|
1864
1905
|
function getCBParameters(b, stdfun) {
|
|
1865
1906
|
let handlerArgs = [];
|
|
@@ -1875,7 +1916,10 @@ var pxt;
|
|
|
1875
1916
|
varName = varBlock && varBlock.getField("VAR").getText();
|
|
1876
1917
|
}
|
|
1877
1918
|
if (varName !== null) {
|
|
1878
|
-
handlerArgs.push(
|
|
1919
|
+
handlerArgs.push({
|
|
1920
|
+
name: varName,
|
|
1921
|
+
type: mkPoint(arg.type)
|
|
1922
|
+
});
|
|
1879
1923
|
}
|
|
1880
1924
|
else {
|
|
1881
1925
|
break;
|
|
@@ -1888,7 +1932,10 @@ var pxt;
|
|
|
1888
1932
|
const varField = b.getField("HANDLER_" + arg.name);
|
|
1889
1933
|
const varName = varField && varField.getText();
|
|
1890
1934
|
if (varName !== null) {
|
|
1891
|
-
handlerArgs.push(
|
|
1935
|
+
handlerArgs.push({
|
|
1936
|
+
name: varName,
|
|
1937
|
+
type: mkPoint(arg.type)
|
|
1938
|
+
});
|
|
1892
1939
|
}
|
|
1893
1940
|
else {
|
|
1894
1941
|
break;
|
|
@@ -2091,11 +2138,7 @@ var pxt;
|
|
|
2091
2138
|
}
|
|
2092
2139
|
if (hasStatementInput(block)) {
|
|
2093
2140
|
const vars = getDeclaredVariables(block, e).map(binding => {
|
|
2094
|
-
return {
|
|
2095
|
-
name: binding[0],
|
|
2096
|
-
type: binding[1],
|
|
2097
|
-
id: id++
|
|
2098
|
-
};
|
|
2141
|
+
return Object.assign(Object.assign({}, binding), { id: id++ });
|
|
2099
2142
|
});
|
|
2100
2143
|
let parentScope = currentScope;
|
|
2101
2144
|
if (vars.length) {
|
|
@@ -2179,17 +2222,37 @@ var pxt;
|
|
|
2179
2222
|
switch (block.type) {
|
|
2180
2223
|
case 'pxt_controls_for':
|
|
2181
2224
|
case 'controls_simple_for':
|
|
2182
|
-
return [
|
|
2225
|
+
return [{
|
|
2226
|
+
name: getLoopVariableField(block).getField("VAR").getText(),
|
|
2227
|
+
type: pNumber
|
|
2228
|
+
}];
|
|
2183
2229
|
case 'pxt_controls_for_of':
|
|
2184
2230
|
case 'controls_for_of':
|
|
2185
|
-
return [
|
|
2231
|
+
return [{
|
|
2232
|
+
name: getLoopVariableField(block).getField("VAR").getText(),
|
|
2233
|
+
type: mkPoint(null)
|
|
2234
|
+
}];
|
|
2235
|
+
case 'function_definition':
|
|
2236
|
+
return block.getArguments().filter(arg => arg.type === "Array")
|
|
2237
|
+
.map(arg => {
|
|
2238
|
+
const point = mkPoint(null);
|
|
2239
|
+
point.isArrayType = true;
|
|
2240
|
+
return {
|
|
2241
|
+
name: arg.name,
|
|
2242
|
+
type: point,
|
|
2243
|
+
isFunctionParameter: true
|
|
2244
|
+
};
|
|
2245
|
+
});
|
|
2186
2246
|
default:
|
|
2187
2247
|
break;
|
|
2188
2248
|
}
|
|
2189
2249
|
if (isMutatingBlock(block)) {
|
|
2190
2250
|
const declarations = block.mutation.getDeclaredVariables();
|
|
2191
2251
|
if (declarations) {
|
|
2192
|
-
return Object.keys(declarations).map(varName =>
|
|
2252
|
+
return Object.keys(declarations).map(varName => ({
|
|
2253
|
+
name: varName,
|
|
2254
|
+
type: mkPoint(declarations[varName])
|
|
2255
|
+
}));
|
|
2193
2256
|
}
|
|
2194
2257
|
}
|
|
2195
2258
|
let stdFunc = e.stdCallTable[block.type];
|
|
@@ -4078,21 +4141,9 @@ var pxt;
|
|
|
4078
4141
|
|| (nsinfo && nsinfo.attributes.color)
|
|
4079
4142
|
|| pxt.toolbox.getNamespaceColor(ns)
|
|
4080
4143
|
|| 255;
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
block.setHelpUrl(helpUrl);
|
|
4085
|
-
}
|
|
4086
|
-
else if (helpUrl !== "none") {
|
|
4087
|
-
block.setHelpUrl("/reference/" + helpUrl);
|
|
4088
|
-
}
|
|
4089
|
-
}
|
|
4090
|
-
else if (fn.pkg && !pxt.appTarget.bundledpkgs[fn.pkg]) { // added package
|
|
4091
|
-
let anchor = fn.qName.toLowerCase().split('.');
|
|
4092
|
-
if (anchor[0] == fn.pkg)
|
|
4093
|
-
anchor.shift();
|
|
4094
|
-
block.setHelpUrl(`/pkg/${fn.pkg}#${encodeURIComponent(anchor.join('-'))}`);
|
|
4095
|
-
}
|
|
4144
|
+
const helpUrl = pxt.blocks.getHelpUrl(fn);
|
|
4145
|
+
if (helpUrl)
|
|
4146
|
+
block.setHelpUrl(helpUrl);
|
|
4096
4147
|
block.setColour(color);
|
|
4097
4148
|
let blockShape = Blockly.OUTPUT_SHAPE_ROUND;
|
|
4098
4149
|
if (fn.retType == "boolean")
|
|
@@ -6324,8 +6375,71 @@ var pxt;
|
|
|
6324
6375
|
Blockly.utils.dom.removeClass(blockPath, 'blockly-ws-search-highlight-pxt');
|
|
6325
6376
|
});
|
|
6326
6377
|
}
|
|
6378
|
+
/**
|
|
6379
|
+
* https://github.com/google/blockly-samples/blob/master/plugins/workspace-search/src/WorkspaceSearch.js#L633
|
|
6380
|
+
*
|
|
6381
|
+
* Modified to center offscreen blocks.
|
|
6382
|
+
*/
|
|
6383
|
+
scrollToVisible_(block) {
|
|
6384
|
+
if (!this.workspace_.isMovable()) {
|
|
6385
|
+
// Cannot scroll to block in a non-movable workspace.
|
|
6386
|
+
return;
|
|
6387
|
+
}
|
|
6388
|
+
// XY is in workspace coordinates.
|
|
6389
|
+
const xy = block.getRelativeToSurfaceXY();
|
|
6390
|
+
const scale = this.workspace_.scale;
|
|
6391
|
+
// Block bounds in pixels relative to the workspace origin (0,0 is centre).
|
|
6392
|
+
const width = block.width * scale;
|
|
6393
|
+
const height = block.height * scale;
|
|
6394
|
+
const top = xy.y * scale;
|
|
6395
|
+
const bottom = (xy.y + block.height) * scale;
|
|
6396
|
+
// In RTL the block's position is the top right of the block, not top left.
|
|
6397
|
+
const left = this.workspace_.RTL ? xy.x * scale - width : xy.x * scale;
|
|
6398
|
+
const right = this.workspace_.RTL ? xy.x * scale : xy.x * scale + width;
|
|
6399
|
+
const metrics = this.workspace_.getMetrics();
|
|
6400
|
+
let targetLeft = metrics.viewLeft;
|
|
6401
|
+
const overflowLeft = left < metrics.viewLeft;
|
|
6402
|
+
const overflowRight = right > metrics.viewLeft + metrics.viewWidth;
|
|
6403
|
+
const wideBlock = width > metrics.viewWidth;
|
|
6404
|
+
if ((!wideBlock && overflowLeft) || (wideBlock && !this.workspace_.RTL)) {
|
|
6405
|
+
// Scroll to show left side of block
|
|
6406
|
+
targetLeft = left;
|
|
6407
|
+
}
|
|
6408
|
+
else if ((!wideBlock && overflowRight) ||
|
|
6409
|
+
(wideBlock && this.workspace_.RTL)) {
|
|
6410
|
+
// Scroll to show right side of block
|
|
6411
|
+
targetLeft = right - metrics.viewWidth;
|
|
6412
|
+
}
|
|
6413
|
+
let targetTop = metrics.viewTop;
|
|
6414
|
+
const overflowTop = top < metrics.viewTop;
|
|
6415
|
+
const overflowBottom = bottom > metrics.viewTop + metrics.viewHeight;
|
|
6416
|
+
const tallBlock = height > metrics.viewHeight;
|
|
6417
|
+
if (overflowTop || (tallBlock && overflowBottom)) {
|
|
6418
|
+
// Scroll to show top of block
|
|
6419
|
+
targetTop = top;
|
|
6420
|
+
}
|
|
6421
|
+
else if (overflowBottom) {
|
|
6422
|
+
// Scroll to show bottom of block
|
|
6423
|
+
targetTop = bottom - metrics.viewHeight;
|
|
6424
|
+
}
|
|
6425
|
+
if (targetLeft !== metrics.viewLeft || targetTop !== metrics.viewTop) {
|
|
6426
|
+
const activeEl = document.activeElement;
|
|
6427
|
+
if (wideBlock || tallBlock) {
|
|
6428
|
+
this.workspace_.scroll(-targetLeft, -targetTop);
|
|
6429
|
+
}
|
|
6430
|
+
else {
|
|
6431
|
+
this.workspace_.centerOnBlock(block.id);
|
|
6432
|
+
}
|
|
6433
|
+
if (activeEl) {
|
|
6434
|
+
// Blockly.WidgetDiv.hide called in scroll is taking away focus.
|
|
6435
|
+
// TODO: Review setFocused call in Blockly.WidgetDiv.hide.
|
|
6436
|
+
activeEl.focus();
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6327
6440
|
open() {
|
|
6328
6441
|
super.open();
|
|
6442
|
+
this.inputElement_.select();
|
|
6329
6443
|
Blockly.utils.dom.addClass(this.workspace_.getInjectionDiv(), 'blockly-ws-searching');
|
|
6330
6444
|
}
|
|
6331
6445
|
close() {
|
|
@@ -12128,15 +12242,20 @@ var pxtblockly;
|
|
|
12128
12242
|
let leftPadding = 0, rightPadding = 0;
|
|
12129
12243
|
switch (outputShape) {
|
|
12130
12244
|
case Blockly.OUTPUT_SHAPE_HEXAGONAL:
|
|
12131
|
-
width =
|
|
12245
|
+
width = size.width / 2;
|
|
12132
12246
|
halfWidth = width / 2;
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12247
|
+
leftPadding = -halfWidth; // total translation when toggle is left-aligned = 0
|
|
12248
|
+
rightPadding = halfWidth - innerWidth; // total translation when right-aligned = width
|
|
12249
|
+
/**
|
|
12250
|
+
* Toggle defined clockwise from bottom left:
|
|
12251
|
+
*
|
|
12252
|
+
* 0, 14 ----------- width, 14
|
|
12253
|
+
* / \
|
|
12254
|
+
* -14, 0 width + 14, 0
|
|
12255
|
+
* \ /
|
|
12256
|
+
* 0, -14 ----------- width, -14
|
|
12257
|
+
*/
|
|
12258
|
+
this.toggleThumb_.setAttribute('points', `${0},-14 -14,0 ${0},14 ${width},14 ${width + 14},0 ${width},-14`);
|
|
12140
12259
|
break;
|
|
12141
12260
|
case Blockly.OUTPUT_SHAPE_ROUND:
|
|
12142
12261
|
case Blockly.OUTPUT_SHAPE_SQUARE:
|
package/built/pxtcompiler.js
CHANGED
|
@@ -4620,6 +4620,8 @@ ${output}</xml>`;
|
|
|
4620
4620
|
return getTaggedTemplateExpression(n);
|
|
4621
4621
|
case SK.CallExpression:
|
|
4622
4622
|
return getStatementBlock(n, undefined, undefined, true);
|
|
4623
|
+
case SK.AsExpression:
|
|
4624
|
+
return getOutputBlock(n.expression);
|
|
4623
4625
|
default:
|
|
4624
4626
|
error(n, pxtc.Util.lf("Unsupported syntax kind for output expression block: {0}", SK[n.kind]));
|
|
4625
4627
|
break;
|
|
@@ -5500,11 +5502,14 @@ ${output}</xml>`;
|
|
|
5500
5502
|
r.mutationChildren = [];
|
|
5501
5503
|
n.parameters.forEach(p => {
|
|
5502
5504
|
const paramName = p.name.getText();
|
|
5505
|
+
let type = normalizeType(p.type.getText());
|
|
5506
|
+
if (pxt.U.endsWith(type, "[]"))
|
|
5507
|
+
type = "Array";
|
|
5503
5508
|
r.mutationChildren.push({
|
|
5504
5509
|
nodeName: "arg",
|
|
5505
5510
|
attributes: {
|
|
5506
5511
|
name: paramName,
|
|
5507
|
-
type
|
|
5512
|
+
type,
|
|
5508
5513
|
id: env.functionParamIds[name][paramName]
|
|
5509
5514
|
}
|
|
5510
5515
|
});
|
|
@@ -5580,11 +5585,14 @@ ${output}</xml>`;
|
|
|
5580
5585
|
env.declaredFunctions[name].parameters.forEach((p, i) => {
|
|
5581
5586
|
const paramName = p.name.getText();
|
|
5582
5587
|
const argId = env.functionParamIds[name][paramName];
|
|
5588
|
+
let type = normalizeType(p.type.getText());
|
|
5589
|
+
if (pxt.U.endsWith(type, "[]"))
|
|
5590
|
+
type = "Array";
|
|
5583
5591
|
r.mutationChildren.push({
|
|
5584
5592
|
nodeName: "arg",
|
|
5585
5593
|
attributes: {
|
|
5586
5594
|
name: paramName,
|
|
5587
|
-
type:
|
|
5595
|
+
type: type,
|
|
5588
5596
|
id: argId
|
|
5589
5597
|
}
|
|
5590
5598
|
});
|
|
@@ -6452,7 +6460,8 @@ ${output}</xml>`;
|
|
|
6452
6460
|
if (!type) {
|
|
6453
6461
|
return pxtc.Util.lf("Function parameters must declare a type");
|
|
6454
6462
|
}
|
|
6455
|
-
|
|
6463
|
+
const normalized = normalizeType(type);
|
|
6464
|
+
if (env.opts.allowedArgumentTypes.indexOf(normalized) === -1 && !pxtc.U.endsWith(normalized, "[]")) {
|
|
6456
6465
|
return pxtc.Util.lf("Only types that can be added in blocks can be used for function arguments");
|
|
6457
6466
|
}
|
|
6458
6467
|
}
|
|
@@ -6727,6 +6736,8 @@ ${output}</xml>`;
|
|
|
6727
6736
|
return checkStatement(n, env, true, undefined);
|
|
6728
6737
|
case SK.TaggedTemplateExpression:
|
|
6729
6738
|
return checkTaggedTemplateExpression(n, env);
|
|
6739
|
+
case SK.AsExpression:
|
|
6740
|
+
return checkAsExpression(n);
|
|
6730
6741
|
}
|
|
6731
6742
|
return pxtc.Util.lf("Unsupported syntax kind for output expression block: {0}", SK[n.kind]);
|
|
6732
6743
|
function checkStringLiteral(n) {
|
|
@@ -6823,6 +6834,31 @@ ${output}</xml>`;
|
|
|
6823
6834
|
// The compiler will have already caught any invalid tags or templates
|
|
6824
6835
|
return undefined;
|
|
6825
6836
|
}
|
|
6837
|
+
function checkAsExpression(n) {
|
|
6838
|
+
// The only time we allow casts to decompile is in the very special case where someone has
|
|
6839
|
+
// written a program comparing two string, boolean, or numeric literals in blocks and
|
|
6840
|
+
// converted to text. e.g. 3 == 5 or true != false
|
|
6841
|
+
if (n.type.getText().trim() === "any" && (ts.isStringOrNumericLiteral(n.expression) ||
|
|
6842
|
+
n.expression.kind === SK.TrueKeyword || n.expression.kind === SK.FalseKeyword)) {
|
|
6843
|
+
const [parent] = getParent(n);
|
|
6844
|
+
if (parent.kind === SK.BinaryExpression) {
|
|
6845
|
+
switch (parent.operatorToken.kind) {
|
|
6846
|
+
case SK.EqualsEqualsToken:
|
|
6847
|
+
case SK.EqualsEqualsEqualsToken:
|
|
6848
|
+
case SK.ExclamationEqualsToken:
|
|
6849
|
+
case SK.ExclamationEqualsEqualsToken:
|
|
6850
|
+
case SK.LessThanToken:
|
|
6851
|
+
case SK.LessThanEqualsToken:
|
|
6852
|
+
case SK.GreaterThanToken:
|
|
6853
|
+
case SK.GreaterThanEqualsToken:
|
|
6854
|
+
return undefined;
|
|
6855
|
+
default:
|
|
6856
|
+
break;
|
|
6857
|
+
}
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
return pxtc.Util.lf("Casting not supported in blocks");
|
|
6861
|
+
}
|
|
6826
6862
|
function getParent(node) {
|
|
6827
6863
|
if (!node.parent) {
|
|
6828
6864
|
return [undefined, node];
|
|
@@ -12615,8 +12651,10 @@ ${lbl}: .short 0xffff
|
|
|
12615
12651
|
return needsNumberConversions() ? pxtc.ir.rtcall("pxt::toInt", [e]) : e;
|
|
12616
12652
|
};
|
|
12617
12653
|
// c = a[i]
|
|
12618
|
-
if (iterVar)
|
|
12654
|
+
if (iterVar) {
|
|
12619
12655
|
proc.emitExpr(iterVar.storeByRef(pxtc.ir.rtcall(indexer, [collectionVar.loadCore(), toInt(intVarIter.loadCore())])));
|
|
12656
|
+
emitBrk(node.initializer);
|
|
12657
|
+
}
|
|
12620
12658
|
flushHoistedFunctionDefinitions();
|
|
12621
12659
|
emit(node.statement);
|
|
12622
12660
|
proc.emitLblDirect(l.cont);
|
package/built/pxtlib.d.ts
CHANGED
|
@@ -552,6 +552,7 @@ declare namespace pxt {
|
|
|
552
552
|
multiUrl?: string;
|
|
553
553
|
asseteditorUrl?: string;
|
|
554
554
|
skillmapUrl?: string;
|
|
555
|
+
authcodeUrl?: string;
|
|
555
556
|
isStatic?: boolean;
|
|
556
557
|
verprefix?: string;
|
|
557
558
|
}
|
|
@@ -656,6 +657,7 @@ declare namespace pxt.blocks {
|
|
|
656
657
|
function normalizeBlock(b: string, err?: (msg: string) => void): string;
|
|
657
658
|
function compileInfo(fn: pxtc.SymbolInfo): BlockCompileInfo;
|
|
658
659
|
function hasHandler(fn: pxtc.SymbolInfo): boolean;
|
|
660
|
+
function getHelpUrl(fn: pxtc.SymbolInfo): string;
|
|
659
661
|
/**
|
|
660
662
|
* Returns which Blockly block type to use for an argument reporter based
|
|
661
663
|
* on the specified TypeScript type.
|
package/built/pxtlib.js
CHANGED
|
@@ -3909,6 +3909,25 @@ var pxt;
|
|
|
3909
3909
|
});
|
|
3910
3910
|
}
|
|
3911
3911
|
blocks.hasHandler = hasHandler;
|
|
3912
|
+
function getHelpUrl(fn) {
|
|
3913
|
+
if (fn.attributes.help) {
|
|
3914
|
+
const helpUrl = fn.attributes.help.replace(/^\//, '');
|
|
3915
|
+
if (/^github:/.test(helpUrl)) {
|
|
3916
|
+
return helpUrl;
|
|
3917
|
+
}
|
|
3918
|
+
else if (helpUrl !== "none") {
|
|
3919
|
+
return "/reference/" + helpUrl;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
else if (fn.pkg && !pxt.appTarget.bundledpkgs[fn.pkg]) { // added package
|
|
3923
|
+
let anchor = fn.qName.toLowerCase().split('.');
|
|
3924
|
+
if (anchor[0] == fn.pkg)
|
|
3925
|
+
anchor.shift();
|
|
3926
|
+
return `/pkg/${fn.pkg}#${encodeURIComponent(anchor.join('-'))}`;
|
|
3927
|
+
}
|
|
3928
|
+
return undefined;
|
|
3929
|
+
}
|
|
3930
|
+
blocks.getHelpUrl = getHelpUrl;
|
|
3912
3931
|
/**
|
|
3913
3932
|
* Returns which Blockly block type to use for an argument reporter based
|
|
3914
3933
|
* on the specified TypeScript type.
|
|
@@ -3920,6 +3939,9 @@ var pxt;
|
|
|
3920
3939
|
if (varType === "boolean" || varType === "number" || varType === "string") {
|
|
3921
3940
|
reporterType = `argument_reporter_${varType}`;
|
|
3922
3941
|
}
|
|
3942
|
+
if (/^(?:Array<(?:.+)>)|(?:(?:.+)\[\])$/.test(varType)) {
|
|
3943
|
+
reporterType = "argument_reporter_array";
|
|
3944
|
+
}
|
|
3923
3945
|
return reporterType;
|
|
3924
3946
|
}
|
|
3925
3947
|
blocks.reporterTypeForArgType = reporterTypeForArgType;
|
package/built/pxtpy.js
CHANGED
|
@@ -955,6 +955,14 @@ var pxt;
|
|
|
955
955
|
FloorDiv: 1,
|
|
956
956
|
Mult: 1, // this can be also used on strings and arrays, but let's ignore that for now
|
|
957
957
|
};
|
|
958
|
+
const arithmeticCompareOps = {
|
|
959
|
+
Eq: 1,
|
|
960
|
+
NotEq: 1,
|
|
961
|
+
Lt: 1,
|
|
962
|
+
LtE: 1,
|
|
963
|
+
Gt: 1,
|
|
964
|
+
GtE: 1
|
|
965
|
+
};
|
|
958
966
|
const opMapping = {
|
|
959
967
|
Add: "+",
|
|
960
968
|
Sub: "-",
|
|
@@ -1826,9 +1834,24 @@ var pxt;
|
|
|
1826
1834
|
let idx = B.mkInfix(expr(n.comparators[0]), ".", B.H.mkCall("indexOf", [expr(n.left)]));
|
|
1827
1835
|
return B.mkInfix(idx, n.ops[0] == "In" ? ">=" : "<", B.mkText("0"));
|
|
1828
1836
|
}
|
|
1829
|
-
let
|
|
1837
|
+
let left = expr(n.left);
|
|
1838
|
+
let right = expr(n.comparators[0]);
|
|
1839
|
+
// Special handling for comparisons of literal types, e.g. 0 === 5
|
|
1840
|
+
const castIfLiteralComparison = (op, leftExpr, rightExpr) => {
|
|
1841
|
+
if (arithmeticCompareOps[op]) {
|
|
1842
|
+
if (isNumStringOrBool(leftExpr) && isNumStringOrBool(rightExpr) && B.flattenNode([left]) !== B.flattenNode([right])) {
|
|
1843
|
+
left = B.H.mkParenthesizedExpression(B.mkGroup([left, B.mkText(" as any")]));
|
|
1844
|
+
right = B.H.mkParenthesizedExpression(B.mkGroup([right, B.mkText(" as any")]));
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
castIfLiteralComparison(n.ops[0], n.left, n.comparators[0]);
|
|
1849
|
+
let r = binop(left, n.ops[0], right);
|
|
1830
1850
|
for (let i = 1; i < n.ops.length; ++i) {
|
|
1831
|
-
|
|
1851
|
+
left = expr(n.comparators[i - 1]);
|
|
1852
|
+
right = expr(n.comparators[i]);
|
|
1853
|
+
castIfLiteralComparison(n.ops[i], n.comparators[i - 1], n.comparators[i]);
|
|
1854
|
+
r = binop(r, "And", binop(left, n.ops[i], right));
|
|
1832
1855
|
}
|
|
1833
1856
|
return r;
|
|
1834
1857
|
},
|
|
@@ -2616,6 +2639,16 @@ var pxt;
|
|
|
2616
2639
|
const t = canonicalize(typeOf(expr));
|
|
2617
2640
|
return t && t.primType === "@array";
|
|
2618
2641
|
}
|
|
2642
|
+
function isNumStringOrBool(expr) {
|
|
2643
|
+
switch (expr.kind) {
|
|
2644
|
+
case "Num":
|
|
2645
|
+
case "Str":
|
|
2646
|
+
return true;
|
|
2647
|
+
case "NameConstant":
|
|
2648
|
+
return expr.value !== null;
|
|
2649
|
+
}
|
|
2650
|
+
return false;
|
|
2651
|
+
}
|
|
2619
2652
|
function buildOverride(override, args, recv) {
|
|
2620
2653
|
const result = [];
|
|
2621
2654
|
for (const part of override.parts) {
|
|
@@ -5987,6 +6020,42 @@ var pxt;
|
|
|
5987
6020
|
}
|
|
5988
6021
|
return fn(s);
|
|
5989
6022
|
}
|
|
6023
|
+
function getParent(node) {
|
|
6024
|
+
if (!node.parent) {
|
|
6025
|
+
return undefined;
|
|
6026
|
+
}
|
|
6027
|
+
else if (node.parent.kind === ts.SyntaxKind.ParenthesizedExpression) {
|
|
6028
|
+
return getParent(node.parent);
|
|
6029
|
+
}
|
|
6030
|
+
else {
|
|
6031
|
+
return node.parent;
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
function isDecompilableAsExpression(n) {
|
|
6035
|
+
// The only time we allow casts to decompile is in the very special case where someone has
|
|
6036
|
+
// written a program comparing two string, boolean, or numeric literals in blocks and
|
|
6037
|
+
// converted to text. e.g. 3 == 5 or true != false
|
|
6038
|
+
if (n.type.getText().trim() === "any" && (ts.isNumericLiteral(n.expression) || ts.isStringLiteral(n.expression) ||
|
|
6039
|
+
n.expression.kind === ts.SyntaxKind.TrueKeyword || n.expression.kind === ts.SyntaxKind.FalseKeyword)) {
|
|
6040
|
+
const parent = getParent(n);
|
|
6041
|
+
if ((parent === null || parent === void 0 ? void 0 : parent.kind) === ts.SyntaxKind.BinaryExpression) {
|
|
6042
|
+
switch (parent.operatorToken.kind) {
|
|
6043
|
+
case ts.SyntaxKind.EqualsEqualsToken:
|
|
6044
|
+
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
6045
|
+
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
6046
|
+
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
6047
|
+
case ts.SyntaxKind.LessThanToken:
|
|
6048
|
+
case ts.SyntaxKind.LessThanEqualsToken:
|
|
6049
|
+
case ts.SyntaxKind.GreaterThanToken:
|
|
6050
|
+
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
6051
|
+
return true;
|
|
6052
|
+
default:
|
|
6053
|
+
break;
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
return false;
|
|
6058
|
+
}
|
|
5990
6059
|
function isConstExp(s) {
|
|
5991
6060
|
let isConst = (s) => {
|
|
5992
6061
|
switch (s.kind) {
|
|
@@ -6069,6 +6138,8 @@ var pxt;
|
|
|
6069
6138
|
return asExpRes(s.getText());
|
|
6070
6139
|
if (ts.isConditionalExpression(s))
|
|
6071
6140
|
return emitCondExp(s);
|
|
6141
|
+
if (ts.isAsExpression(s) && isDecompilableAsExpression(s))
|
|
6142
|
+
return emitExp(s.expression);
|
|
6072
6143
|
// TODO handle more expressions
|
|
6073
6144
|
pxt.tickEvent("depython.todo.expression", { kind: s.kind });
|
|
6074
6145
|
// return asExpRes(s.getText(), ["# unknown expression: " + s.kind]) // uncomment for easier locating
|
package/built/server.js
CHANGED
|
@@ -1046,6 +1046,10 @@ function serveAsync(options) {
|
|
|
1046
1046
|
sendFile(path.join(publicDir, 'skillmap.html'));
|
|
1047
1047
|
return;
|
|
1048
1048
|
}
|
|
1049
|
+
if (pathname == "/--authcode") {
|
|
1050
|
+
sendFile(path.join(publicDir, 'authcode.html'));
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1049
1053
|
if (/\/-[-]*docs.*$/.test(pathname)) {
|
|
1050
1054
|
sendFile(path.join(publicDir, 'docs.html'));
|
|
1051
1055
|
return;
|