Skip to content

Commit 7dd778b

Browse files
Workspace comments! (microsoft#4067)
* Integrate workspace comments with PXT.
1 parent 7bbbeaa commit 7dd778b

24 files changed

+805
-1591
lines changed

localtypings/blockly.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ declare namespace Blockly {
752752
setTooltip(newTip: string | (() => void)): void;
753753
// Passing null will delete current text
754754
setWarningText(text: string): void;
755+
setHighlightWarning(isHighlightingWarning: boolean): void;
755756
isEditable(): boolean;
756757
isInsertionMarker(): boolean;
757758
isShadow(): boolean;
@@ -764,6 +765,18 @@ declare namespace Blockly {
764765
getSvgRoot(): Element;
765766
}
766767

768+
class WorkspaceComment {
769+
getContent(): string;
770+
771+
getRelativeToSurfaceXY(): goog.math.Coordinate;
772+
moveBy(x: number, y: number): void;
773+
getHeightWidth(): { width: number; height: number; };
774+
getBoundingRectangle(): {
775+
topLeft: goog.math.Coordinate;
776+
bottomRight: goog.math.Coordinate;
777+
}
778+
}
779+
767780
class Comment extends Icon {
768781
constructor(b: Block);
769782

@@ -893,6 +906,7 @@ declare namespace Blockly {
893906
clear(): void;
894907
dispose(): void;
895908
getTopBlocks(ordered: boolean): Block[];
909+
getTopComments(ordered: boolean): WorkspaceComment[];
896910
getBlockById(id: string): Block;
897911
getAllBlocks(): Block[];
898912
traceOn(armed: boolean): void;
@@ -905,6 +919,7 @@ declare namespace Blockly {
905919
zoomCenter(type: number): void;
906920
scrollCenter(): void;
907921
highlightBlock(id: string): void;
922+
centerOnBlock(id: string): void;
908923
glowBlock(id: string, state: boolean): void;
909924
glowStack(id: string, state: boolean): void;
910925
undo(redo?: boolean): void;

pxtblocks/blocklycompiler.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ namespace pxt.blocks {
156156
return find((<any>b).p);
157157

158158
if (b.type == "variables_get")
159-
return find(lookup(e, escapeVarName(b.getFieldValue("VAR"), e)).type);
159+
return find(lookup(e, escapeVarName(b.getField("VAR").getText(), e)).type);
160160

161161
if (!b.outputConnection) {
162162
return ground(pUnit.type);
@@ -346,12 +346,12 @@ namespace pxt.blocks {
346346
case "controls_for_of":
347347
unionParam(e, b, "LIST", ground("Array"));
348348
const listTp = returnType(e, getInputTargetBlock(b, "LIST"));
349-
const elementTp = lookup(e, escapeVarName(b.getFieldValue("VAR"), e)).type;
349+
const elementTp = lookup(e, escapeVarName(b.getField("VAR").getText(), e)).type;
350350
genericLink(listTp, elementTp);
351351
break;
352352
case "variables_set":
353353
case "variables_change":
354-
let x = escapeVarName(b.getFieldValue("VAR"), e);
354+
let x = escapeVarName(b.getField("VAR").getText(), e);
355355
let p1 = lookup(e, x).type;
356356
attachPlaceholderIf(e, b, "VALUE");
357357
let rhs = getInputTargetBlock(b, "VALUE");
@@ -685,6 +685,11 @@ namespace pxt.blocks {
685685
return mkStmt(mkText(name + "()"));
686686
}
687687

688+
function compileWorkspaceComment(c: B.WorkspaceComment): JsNode {
689+
const content = c.getContent();
690+
return Helpers.mkMultiComment(content);
691+
}
692+
688693
function defaultValueForType(t: Point): JsNode {
689694
if (t.type == null) {
690695
union(t, ground(pNumber.type));
@@ -906,7 +911,7 @@ namespace pxt.blocks {
906911
}
907912

908913
function compileControlsFor(e: Environment, b: B.Block, comments: string[]): JsNode[] {
909-
let bVar = escapeVarName(b.getFieldValue("VAR"), e);
914+
let bVar = escapeVarName(b.getField("VAR").getText(), e);
910915
let bTo = getInputTargetBlock(b, "TO");
911916
let bDo = getInputTargetBlock(b, "DO");
912917
let bBy = getInputTargetBlock(b, "BY");
@@ -955,7 +960,7 @@ namespace pxt.blocks {
955960
}
956961

957962
function compileControlsForOf(e: Environment, b: B.Block, comments: string[]) {
958-
let bVar = escapeVarName(b.getFieldValue("VAR"), e);
963+
let bVar = escapeVarName(b.getField("VAR").getText(), e);
959964
let bOf = getInputTargetBlock(b, "LIST");
960965
let bDo = getInputTargetBlock(b, "DO");
961966

@@ -1012,7 +1017,7 @@ namespace pxt.blocks {
10121017
}
10131018

10141019
function compileVariableGet(e: Environment, b: B.Block): JsNode {
1015-
let name = escapeVarName(b.getFieldValue("VAR"), e);
1020+
let name = escapeVarName(b.getField("VAR").getText(), e);
10161021
let binding = lookup(e, name);
10171022
if (!binding.assigned)
10181023
binding.assigned = VarUsage.Read;
@@ -1021,7 +1026,7 @@ namespace pxt.blocks {
10211026
}
10221027

10231028
function compileSet(e: Environment, b: B.Block, comments: string[]): JsNode {
1024-
let bVar = escapeVarName(b.getFieldValue("VAR"), e);
1029+
let bVar = escapeVarName(b.getField("VAR").getText(), e);
10251030
let bExpr = getInputTargetBlock(b, "VALUE");
10261031
let binding = lookup(e, bVar);
10271032
let isDef = false
@@ -1041,7 +1046,7 @@ namespace pxt.blocks {
10411046
}
10421047

10431048
function compileChange(e: Environment, b: B.Block, comments: string[]): JsNode {
1044-
let bVar = escapeVarName(b.getFieldValue("VAR"), e);
1049+
let bVar = escapeVarName(b.getField("VAR").getText(), e);
10451050
let bExpr = getInputTargetBlock(b, "VALUE");
10461051
let binding = lookup(e, bVar);
10471052
if (!binding.assigned)
@@ -1447,7 +1452,7 @@ namespace pxt.blocks {
14471452
if (!b)
14481453
return false;
14491454
else if ((b.type == "controls_for" || b.type == "controls_simple_for" || b.type == "controls_for_of")
1450-
&& escapeVarName(b.getFieldValue("VAR"), e) == name)
1455+
&& escapeVarName(b.getField("VAR").getText(), e) == name)
14511456
return true;
14521457
else if (isMutatingBlock(b) && b.mutation.isDeclaredByMutation(name))
14531458
return true;
@@ -1486,7 +1491,7 @@ namespace pxt.blocks {
14861491
// collect local variables.
14871492
if (w) w.getAllBlocks().filter(b => !b.disabled).forEach(b => {
14881493
if (b.type == "controls_for" || b.type == "controls_simple_for" || b.type == "controls_for_of") {
1489-
let x = escapeVarName(b.getFieldValue("VAR"), e);
1494+
let x = escapeVarName(b.getField("VAR").getText(), e);
14901495
if (b.type == "controls_for_of") {
14911496
trackLocalDeclaration(x, null);
14921497
}
@@ -1518,7 +1523,7 @@ namespace pxt.blocks {
15181523
// set block, 1) make sure that the variable is bound, then 2) mark the variable if needed.
15191524
if (w) w.getAllBlocks().filter(b => !b.disabled).forEach(b => {
15201525
if (b.type == "variables_get" || b.type == "variables_set" || b.type == "variables_change") {
1521-
let x = escapeVarName(b.getFieldValue("VAR"), e);
1526+
let x = escapeVarName(b.getField("VAR").getText(), e);
15221527
if (lookup(e, x) == null)
15231528
e = extend(e, x, null);
15241529

@@ -1608,7 +1613,16 @@ namespace pxt.blocks {
16081613
return mkStmt(mkText("let " + b.name + tp + " = "), defl)
16091614
});
16101615

1611-
return stmtsVariables.concat(stmtsMain)
1616+
const allStmts = stmtsVariables.concat(stmtsMain);
1617+
1618+
// compile workspace comments, add them to the top
1619+
const commentStmts: JsNode[] = [];
1620+
const topComments = w.getTopComments(true)
1621+
topComments.forEach(c => {
1622+
append(commentStmts, compileWorkspaceComment(c).children);
1623+
})
1624+
1625+
return commentStmts.concat(allStmts);
16121626
} catch (err) {
16131627
let be: B.Block = (err as any).block;
16141628
if (be) {

pxtblocks/blocklylayout.ts

+36-19
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace pxt.blocks.layout {
1919
Util.toArray(oldDom.childNodes)
2020
.filter(n => n.nodeType == Node.ELEMENT_NODE && n.localName == "block" && (<Element>n).getAttribute("disabled") == "true")
2121
.forEach(n => newDom.appendChild(newDom.ownerDocument.importNode(n, true)));
22-
const updatedXml = Blockly.Xml.domToPrettyText(newDom);
22+
const updatedXml = Blockly.Xml.domToText(newDom);
2323
return updatedXml;
2424
}
2525

@@ -51,8 +51,14 @@ namespace pxt.blocks.layout {
5151
declare function unescape(escapeUri: string): string;
5252

5353
export function verticalAlign(ws: B.Workspace, emPixels: number) {
54-
let blocks = ws.getTopBlocks(true);
5554
let y = 0
55+
let comments = ws.getTopComments(true);
56+
comments.forEach(comment => {
57+
comment.moveBy(0, y)
58+
y += comment.getHeightWidth().height
59+
y += emPixels; //buffer
60+
})
61+
let blocks = ws.getTopBlocks(true);
5662
blocks.forEach(block => {
5763
block.moveBy(0, y)
5864
y += block.getHeightWidth().height
@@ -67,14 +73,14 @@ namespace pxt.blocks.layout {
6773

6874
// Only use the width if in portrait, otherwise the blocks are too spread out
6975
if (metrics.viewHeight > metrics.viewWidth) {
70-
flowBlocks(ws.getTopBlocks(true), undefined, metrics.viewWidth)
76+
flowBlocks(ws.getTopComments(true), ws.getTopBlocks(true), undefined, metrics.viewWidth)
7177
return;
7278
}
7379
}
74-
flowBlocks(ws.getTopBlocks(true), opts.ratio);
80+
flowBlocks(ws.getTopComments(true), ws.getTopBlocks(true), opts.ratio);
7581
}
7682
else {
77-
flowBlocks(ws.getTopBlocks(true));
83+
flowBlocks(ws.getTopComments(true), ws.getTopBlocks(true));
7884
}
7985
}
8086

@@ -221,7 +227,7 @@ namespace pxt.blocks.layout {
221227
return Promise.all(p).then(() => { })
222228
}
223229

224-
function flowBlocks(blocks: Blockly.Block[], ratio: number = 1.62, maxWidth?: number) {
230+
function flowBlocks(comments: Blockly.WorkspaceComment[], blocks: Blockly.Block[], ratio: number = 1.62, maxWidth?: number) {
225231
const gap = 16;
226232
const marginx = 20;
227233
const marginy = 20;
@@ -232,28 +238,39 @@ namespace pxt.blocks.layout {
232238
}
233239
else {
234240
// compute total block surface and infer width
235-
let surface = 0;
241+
let commentSurface = 0;
242+
let blockSurface = 0;
243+
for (let comment of comments) {
244+
let s = comment.getHeightWidth();
245+
commentSurface += s.width * s.height;
246+
}
236247
for (let block of blocks) {
237248
let s = block.getHeightWidth();
238-
surface += s.width * s.height;
249+
blockSurface += s.width * s.height;
239250
}
240-
maxx = Math.sqrt(surface) * ratio;
251+
maxx = Math.sqrt(Math.max(commentSurface, blockSurface)) * ratio;
241252
}
242253

243254
let insertx = marginx;
244255
let inserty = marginy;
245256
let endy = 0;
246-
for (let block of blocks) {
247-
let r = block.getBoundingRectangle();
248-
let s = block.getHeightWidth();
249-
// move block to insertion point
250-
block.moveBy(insertx - r.topLeft.x, inserty - r.topLeft.y);
251-
insertx += s.width + gap;
252-
endy = Math.max(endy, inserty + s.height + gap);
253-
if (insertx > maxx) { // start new line
254-
insertx = marginx;
255-
inserty = endy;
257+
function flowBlocksInternal(blocks: Blockly.Block[] | Blockly.WorkspaceComment[]) {
258+
for (let block of blocks) {
259+
let r = block.getBoundingRectangle();
260+
let s = block.getHeightWidth();
261+
// move block to insertion point
262+
block.moveBy(insertx - r.topLeft.x, inserty - r.topLeft.y);
263+
insertx += s.width + gap;
264+
endy = Math.max(endy, inserty + s.height + gap);
265+
if (insertx > maxx) { // start new line
266+
insertx = marginx;
267+
inserty = endy;
268+
}
256269
}
257270
}
271+
flowBlocksInternal(comments);
272+
insertx = marginx;
273+
inserty = endy || marginy;
274+
flowBlocksInternal(blocks);
258275
}
259276
}

pxtblocks/blocklyloader.ts

+12
Original file line numberDiff line numberDiff line change
@@ -1914,6 +1914,7 @@ namespace pxt.blocks {
19141914
initLogic();
19151915
initText();
19161916
initDrag();
1917+
initComments();
19171918
}
19181919

19191920
function setBuiltinHelpInfo(block: any, id: string) {
@@ -2216,6 +2217,12 @@ namespace pxt.blocks {
22162217
let menuOptions: Blockly.ContextMenu.MenuItem[] = [];
22172218
let topBlocks = this.getTopBlocks(true);
22182219
let eventGroup = Blockly.utils.genUid();
2220+
let ws = this;
2221+
2222+
// Option to add a workspace comment.
2223+
if (this.options.comments) {
2224+
menuOptions.push((Blockly.ContextMenu as any).workspaceCommentOption(ws, e));
2225+
}
22192226

22202227
// Add a little animation to collapsing and expanding.
22212228
const DELAY = 10;
@@ -3012,6 +3019,7 @@ namespace pxt.blocks {
30123019
.appendField('', 'PARAMS');
30133020
this.setColour(getNamespaceColor('functions'));
30143021
this.arguments_ = [];
3022+
this.argumentVarModels_ = [];
30153023
this.setStartHat(true);
30163024
this.setStatements_(true);
30173025
this.statementConnection_ = null;
@@ -3580,4 +3588,8 @@ namespace pxt.blocks {
35803588
(filters.blocks[blockId] === FilterState.Disabled || filters.blocks[blockId] === FilterState.Hidden);
35813589
return !isNamespaceFiltered && !isBlockFiltered;
35823590
}
3591+
3592+
function initComments() {
3593+
(Blockly.Msg as any).WORKSPACE_COMMENT_DEFAULT_TEXT = '';
3594+
}
35833595
}

0 commit comments

Comments
 (0)