Skip to content

Commit 49b9109

Browse files
feat: heuristic insert (#355)
* chore: simplify tryToConnectNodes We only insert blocks so remove other cases. * feat: heurstic insertion logic - use first statement or value input - in statement inputs move to the last next connection in the chain - use next connections over previous connections
1 parent 841ae3c commit 49b9109

File tree

4 files changed

+41
-112
lines changed

4 files changed

+41
-112
lines changed

src/actions/clipboard.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,7 @@ export class Clipboard {
376376
const block = clipboard.paste(this.copyData, pasteWorkspace) as BlockSvg;
377377
if (block) {
378378
if (targetNode) {
379-
this.navigation.tryToConnectNodes(
380-
pasteWorkspace,
381-
targetNode,
382-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
383-
ASTNode.createBlockNode(block)!,
384-
);
379+
this.navigation.tryToConnectBlock(targetNode, block);
385380
}
386381
Events.setGroup(false);
387382
return true;

src/actions/enter.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,7 @@ export class EnterAction {
130130
const newBlock = this.createNewBlock(workspace);
131131
if (!newBlock) return;
132132
if (stationaryNode) {
133-
if (
134-
!this.navigation.tryToConnectNodes(
135-
workspace,
136-
stationaryNode,
137-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
138-
ASTNode.createBlockNode(newBlock)!,
139-
)
140-
) {
133+
if (!this.navigation.tryToConnectBlock(stationaryNode, newBlock)) {
141134
console.warn(
142135
'Something went wrong while inserting a block from the flyout.',
143136
);

src/actions/insert.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,8 @@ export class InsertAction {
7878
*/
7979
private registerContextMenuAction() {
8080
const insertAboveItem: ContextMenuRegistry.RegistryItem = {
81-
displayText: (scope) => {
82-
if (scope.block?.previousConnection) {
83-
return 'Insert Block Above (I)';
84-
} else {
85-
return 'Insert Block (I)';
86-
}
81+
displayText: () => {
82+
return 'Insert Block (I)';
8783
},
8884
preconditionFn: (scope: ScopeWithConnection) => {
8985
const block = scope.block ?? scope.connection?.getSourceBlock();

src/navigation.ts

Lines changed: 37 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -675,113 +675,63 @@ export class Navigation {
675675
* Tries to intelligently connect the blocks or connections
676676
* represented by the given nodes, based on node types and locations.
677677
*
678-
* @param workspace The main workspace.
679678
* @param stationaryNode The first node to connect.
680-
* @param movingNode The second node to connect.
679+
* @param movingBlock The block we're moving.
681680
* @returns True if the key was handled; false if something went
682681
* wrong.
683682
*/
684-
tryToConnectNodes(
685-
workspace: Blockly.WorkspaceSvg,
683+
tryToConnectBlock(
686684
stationaryNode: Blockly.ASTNode,
687-
movingNode: Blockly.ASTNode,
685+
movingBlock: Blockly.BlockSvg,
688686
): boolean {
689-
if (!this.logConnectionWarning(stationaryNode, movingNode)) {
690-
return false;
691-
}
692-
693687
const stationaryType = stationaryNode.getType();
694-
const movingType = movingNode.getType();
695-
696688
const stationaryLoc = stationaryNode.getLocation();
697-
const movingLoc = movingNode.getLocation();
698689

699690
if (stationaryNode.isConnection()) {
700-
if (movingNode.isConnection()) {
701-
const stationaryAsConnection =
702-
stationaryLoc as Blockly.RenderedConnection;
703-
const movingAsConnection = movingLoc as Blockly.RenderedConnection;
704-
return this.connect(movingAsConnection, stationaryAsConnection);
705-
}
706691
// Connect the moving block to the stationary connection using
707692
// the most plausible connection on the moving block.
708-
if (
709-
movingType === Blockly.ASTNode.types.BLOCK ||
710-
movingType === Blockly.ASTNode.types.STACK
711-
) {
712-
const stationaryAsConnection =
713-
stationaryLoc as Blockly.RenderedConnection;
714-
const movingAsBlock = movingLoc as Blockly.BlockSvg;
715-
return this.insertBlock(movingAsBlock, stationaryAsConnection);
716-
}
693+
const stationaryAsConnection =
694+
stationaryLoc as Blockly.RenderedConnection;
695+
return this.insertBlock(movingBlock, stationaryAsConnection);
717696
} else if (stationaryType === Blockly.ASTNode.types.WORKSPACE) {
718-
const block = movingNode
719-
? (movingNode.getSourceBlock() as Blockly.BlockSvg)
720-
: null;
721-
return this.moveBlockToWorkspace(block, stationaryNode);
722-
} else if (
723-
stationaryType === Blockly.ASTNode.types.BLOCK &&
724-
movingType === Blockly.ASTNode.types.BLOCK
725-
) {
726-
// Insert the moving block above the stationary block, if the
727-
// appropriate connections exist.
697+
return this.moveBlockToWorkspace(movingBlock, stationaryNode);
698+
} else if (stationaryType === Blockly.ASTNode.types.BLOCK) {
728699
const stationaryBlock = stationaryLoc as Blockly.BlockSvg;
729-
const movingBlock = movingLoc as Blockly.BlockSvg;
730-
if (stationaryBlock.previousConnection) {
700+
701+
// 1. Connect blocks to first compatible input
702+
const inputType = movingBlock.outputConnection
703+
? Blockly.inputs.inputTypes.VALUE
704+
: Blockly.inputs.inputTypes.STATEMENT;
705+
const compatibleInputs = stationaryBlock.inputList.filter(
706+
(input) => input.type === inputType,
707+
);
708+
const input = compatibleInputs.length > 0 ? compatibleInputs[0] : null;
709+
let connection = input?.connection;
710+
if (connection) {
711+
if (inputType === Blockly.inputs.inputTypes.STATEMENT) {
712+
while (connection.targetBlock()?.nextConnection) {
713+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
714+
connection = connection.targetBlock()!.nextConnection!;
715+
}
716+
}
731717
return this.insertBlock(
732718
movingBlock,
733-
stationaryBlock.previousConnection,
719+
connection as Blockly.RenderedConnection,
734720
);
735-
} else if (stationaryBlock.outputConnection) {
736-
return this.insertBlock(movingBlock, stationaryBlock.outputConnection);
737721
}
738-
}
739-
this.warn('Unexpected state in tryToConnectNodes.');
740-
return false;
741-
}
742-
743-
/**
744-
* Warns the user if the given cursor or marker node can not be connected.
745-
*
746-
* @param markerNode The node to try to connect to.
747-
* @param cursorNode The node to connect to the markerNode.
748-
* @returns True if the marker and cursor are valid types, false
749-
* otherwise.
750-
*/
751-
logConnectionWarning(
752-
markerNode: Blockly.ASTNode,
753-
cursorNode: Blockly.ASTNode,
754-
): boolean {
755-
if (!markerNode) {
756-
this.warn('Cannot insert with no marked node.');
757-
return false;
758-
}
759722

760-
if (!cursorNode) {
761-
this.warn('Cannot insert with no cursor node.');
762-
return false;
763-
}
764-
const markerType = markerNode.getType();
765-
const cursorType = cursorNode.getType();
766-
767-
// Check the marker for invalid types.
768-
if (markerType === Blockly.ASTNode.types.FIELD) {
769-
this.warn('Should not have been able to mark a field.');
770-
return false;
771-
} else if (markerType === Blockly.ASTNode.types.STACK) {
772-
this.warn('Should not have been able to mark a stack.');
773-
return false;
774-
}
723+
// 2. Connect statement blocks to next connection.
724+
if (stationaryBlock.nextConnection && !movingBlock.outputConnection) {
725+
return this.insertBlock(movingBlock, stationaryBlock.nextConnection);
726+
}
775727

776-
// Check the cursor for invalid types.
777-
if (cursorType === Blockly.ASTNode.types.FIELD) {
778-
this.warn('Cannot attach a field to anything else.');
779-
return false;
780-
} else if (cursorType === Blockly.ASTNode.types.WORKSPACE) {
781-
this.warn('Cannot attach a workspace to anything else.');
782-
return false;
728+
// 3. Output connection. This will wrap around or displace.
729+
if (stationaryBlock.outputConnection) {
730+
return this.insertBlock(movingBlock, stationaryBlock.outputConnection);
731+
}
783732
}
784-
return true;
733+
this.warn(`Unexpected case in tryToConnectBlock ${stationaryType}.`);
734+
return false;
785735
}
786736

787737
/**
@@ -1144,12 +1094,7 @@ export class Navigation {
11441094
) as Blockly.BlockSvg;
11451095
if (block) {
11461096
if (targetNode) {
1147-
this.tryToConnectNodes(
1148-
workspace,
1149-
targetNode,
1150-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1151-
Blockly.ASTNode.createBlockNode(block)!,
1152-
);
1097+
this.tryToConnectBlock(targetNode, block);
11531098
}
11541099
return true;
11551100
}

0 commit comments

Comments
 (0)