1414 */
1515
1616import * as Blockly from 'blockly/core' ;
17- import { ASTNode , BasicCursor } from 'blockly/core' ;
17+ import { ASTNode , Marker } from 'blockly/core' ;
1818
1919/**
2020 * Class for a line cursor.
2121 */
22- export class LineCursor extends BasicCursor {
22+ export class LineCursor extends Marker {
23+ override type = 'cursor' ;
24+
2325 /**
2426 * Constructor for a line cursor.
2527 */
@@ -33,14 +35,13 @@ export class LineCursor extends BasicCursor {
3335 *
3436 * @returns The next node, or null if the current node is
3537 * not set or there is no next value.
36- * @override
3738 */
3839 next ( ) : ASTNode | null {
3940 const curNode = this . getCurNode ( ) ;
4041 if ( ! curNode ) {
4142 return null ;
4243 }
43- let newNode = this . getNextNode_ ( curNode , this . validLineNode ) ;
44+ let newNode = this . getNextNode ( curNode , this . validLineNode ) ;
4445
4546 // Skip the input or next value if there is a connected block.
4647 if (
@@ -49,7 +50,7 @@ export class LineCursor extends BasicCursor {
4950 newNode . getType ( ) == ASTNode . types . NEXT ) &&
5051 ( newNode . getLocation ( ) as Blockly . Connection ) . targetBlock ( )
5152 ) {
52- newNode = this . getNextNode_ ( newNode , this . validLineNode ) ;
53+ newNode = this . getNextNode ( newNode , this . validLineNode ) ;
5354 }
5455 if ( newNode ) {
5556 this . setCurNode ( newNode ) ;
@@ -64,12 +65,12 @@ export class LineCursor extends BasicCursor {
6465 * @returns The next node, or null if the current node is
6566 * not set or there is no next value.
6667 */
67- override in ( ) : ASTNode | null {
68+ in ( ) : ASTNode | null {
6869 const curNode = this . getCurNode ( ) ;
6970 if ( ! curNode ) {
7071 return null ;
7172 }
72- const newNode = this . getNextNode_ ( curNode , this . validInLineNode ) ;
73+ const newNode = this . getNextNode ( curNode , this . validInLineNode ) ;
7374
7475 if ( newNode ) {
7576 this . setCurNode ( newNode ) ;
@@ -83,40 +84,41 @@ export class LineCursor extends BasicCursor {
8384 * @returns The previous node, or null if the current node
8485 * is not set or there is no previous value.
8586 */
86- override prev ( ) : ASTNode | null {
87+ prev ( ) : ASTNode | null {
8788 const curNode = this . getCurNode ( ) ;
8889 if ( ! curNode ) {
8990 return null ;
9091 }
91- let newNode = this . getPreviousNode_ ( curNode , this . validLineNode ) ;
92+ let newNode = this . getPreviousNode ( curNode , this . validLineNode ) ;
9293
9394 if (
9495 newNode &&
9596 ( newNode . getType ( ) == ASTNode . types . INPUT ||
9697 newNode . getType ( ) == ASTNode . types . NEXT ) &&
9798 ( newNode . getLocation ( ) as Blockly . Connection ) . targetBlock ( )
9899 ) {
99- newNode = this . getPreviousNode_ ( newNode , this . validLineNode ) ;
100+ newNode = this . getPreviousNode ( newNode , this . validLineNode ) ;
100101 }
101102
102103 if ( newNode ) {
103104 this . setCurNode ( newNode ) ;
104105 }
105106 return newNode ;
106107 }
108+
107109 /**
108110 * Moves the cursor to the previous input connection or field in the pre order
109111 * traversal.
110112 *
111113 * @returns The previous node, or null if the current node
112114 * is not set or there is no previous value.
113115 */
114- override out ( ) : ASTNode | null {
116+ out ( ) : ASTNode | null {
115117 const curNode = this . getCurNode ( ) ;
116118 if ( ! curNode ) {
117119 return null ;
118120 }
119- const newNode = this . getPreviousNode_ ( curNode , this . validInLineNode ) ;
121+ const newNode = this . getPreviousNode ( curNode , this . validInLineNode ) ;
120122
121123 if ( newNode ) {
122124 this . setCurNode ( newNode ) ;
@@ -308,6 +310,106 @@ export class LineCursor extends BasicCursor {
308310 }
309311 return newNode ;
310312 }
313+
314+ /**
315+ * Uses pre order traversal to navigate the Blockly AST. This will allow
316+ * a user to easily navigate the entire Blockly AST without having to go in
317+ * and out levels on the tree.
318+ *
319+ * @param node The current position in the AST.
320+ * @param isValid A function true/false depending on whether the given node
321+ * should be traversed.
322+ * @returns The next node in the traversal.
323+ */
324+ private getNextNode (
325+ node : ASTNode | null ,
326+ isValid : ( p1 : ASTNode | null ) => boolean ,
327+ ) : ASTNode | null {
328+ if ( ! node ) {
329+ return null ;
330+ }
331+ const newNode = node . in ( ) || node . next ( ) ;
332+ if ( isValid ( newNode ) ) {
333+ return newNode ;
334+ } else if ( newNode ) {
335+ return this . getNextNode ( newNode , isValid ) ;
336+ }
337+ const siblingOrParent = this . findSiblingOrParent ( node . out ( ) ) ;
338+ if ( isValid ( siblingOrParent ) ) {
339+ return siblingOrParent ;
340+ } else if ( siblingOrParent ) {
341+ return this . getNextNode ( siblingOrParent , isValid ) ;
342+ }
343+ return null ;
344+ }
345+
346+ /**
347+ * Reverses the pre order traversal in order to find the previous node. This
348+ * will allow a user to easily navigate the entire Blockly AST without having
349+ * to go in and out levels on the tree.
350+ *
351+ * @param node The current position in the AST.
352+ * @param isValid A function true/false depending on whether the given node
353+ * should be traversed.
354+ * @returns The previous node in the traversal or null if no previous node
355+ * exists.
356+ */
357+ private getPreviousNode (
358+ node : ASTNode | null ,
359+ isValid : ( p1 : ASTNode | null ) => boolean ,
360+ ) : ASTNode | null {
361+ if ( ! node ) {
362+ return null ;
363+ }
364+ let newNode : ASTNode | null = node . prev ( ) ;
365+
366+ if ( newNode ) {
367+ newNode = this . getRightMostChild ( newNode ) ;
368+ } else {
369+ newNode = node . out ( ) ;
370+ }
371+ if ( isValid ( newNode ) ) {
372+ return newNode ;
373+ } else if ( newNode ) {
374+ return this . getPreviousNode ( newNode , isValid ) ;
375+ }
376+ return null ;
377+ }
378+
379+ /**
380+ * From the given node find either the next valid sibling or parent.
381+ *
382+ * @param node The current position in the AST.
383+ * @returns The parent AST node or null if there are no valid parents.
384+ */
385+ private findSiblingOrParent ( node : ASTNode | null ) : ASTNode | null {
386+ if ( ! node ) {
387+ return null ;
388+ }
389+ const nextNode = node . next ( ) ;
390+ if ( nextNode ) {
391+ return nextNode ;
392+ }
393+ return this . findSiblingOrParent ( node . out ( ) ) ;
394+ }
395+
396+ /**
397+ * Get the right most child of a node.
398+ *
399+ * @param node The node to find the right most child of.
400+ * @returns The right most child of the given node, or the node if no child
401+ * exists.
402+ */
403+ private getRightMostChild ( node : ASTNode | null ) : ASTNode | null {
404+ if ( ! node ! . in ( ) ) {
405+ return node ;
406+ }
407+ let newNode = node ! . in ( ) ;
408+ while ( newNode && newNode . next ( ) ) {
409+ newNode = newNode . next ( ) ;
410+ }
411+ return this . getRightMostChild ( newNode ) ;
412+ }
311413}
312414
313415export const registrationName = 'LineCursor' ;
0 commit comments