Files
blockly/core/keyboard_nav/basic_cursor.ts
Aaron Dodson 3a36ed5116 refactor: Remove more uses of AnyDuringMigration. (#6383)
* refactor: Remove uses of AnyDuringMigration from flyout_base.ts.

* refactor: Remove uses of AnyDuringMigration from flyout_metrics_manager.ts.

* refactor: Remove uses of AnyDuringMigration from variables_dynamic.ts.

* refactor: Remove uses of AnyDuringMigration from procedures.ts.

* refactor: Remove uses of AnyDuringMigration from generator.ts.

* refactor: Remove some uses of AnyDuringMigration from menu.ts.

* refactor: Remove uses of AnyDuringMigration from mutator.ts.

* refactor: Remove uses of AnyDuringMigration from variables.ts.

* refactor: Remove uses of AnyDuringMigration from array.ts.

* refactor: Remove uses of AnyDuringMigration from aria.ts.

* refactor: Remove uses of AnyDuringMigration in basic_cursor.ts.

* refactor: Remove uses of AnyDuringMigration in dropdowndiv.ts.

* refactor: Remove uses of AnyDuringMigration in utils.ts.

* refactor: Remove uses of AnyDuringMigration from menuitem.ts.

* refactor: Remove uses of AnyDuringMigration from idgenerator.ts.

* refactor: Remove uses of AnyDuringMigration in block_animations.ts.

* refactor: Initialize definitions and functionNames in generator.ts by default.
2022-08-24 16:06:38 -07:00

219 lines
5.9 KiB
TypeScript

/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* The class representing a basic cursor.
* Used to demo switching between different cursors.
*
* @class
*/
import * as goog from '../../closure/goog/goog.js';
goog.declareModuleId('Blockly.BasicCursor');
import * as registry from '../registry.js';
import {ASTNode} from './ast_node.js';
import {Cursor} from './cursor.js';
/**
* Class for a basic cursor.
* This will allow the user to get to all nodes in the AST by hitting next or
* previous.
*
* @alias Blockly.BasicCursor
*/
export class BasicCursor extends Cursor {
/** Name used for registering a basic cursor. */
static readonly registrationName = 'basicCursor';
/** @alias Blockly.BasicCursor */
constructor() {
super();
}
/**
* Find the next node in the pre order traversal.
*
* @returns The next node, or null if the current node is not set or there is
* no next value.
*/
override next(): ASTNode|null {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
const newNode = this.getNextNode_(curNode, this.validNode_);
if (newNode) {
this.setCurNode(newNode);
}
return newNode;
}
/**
* For a basic cursor we only have the ability to go next and previous, so
* in will also allow the user to get to the next node in the pre order
* traversal.
*
* @returns The next node, or null if the current node is not set or there is
* no next value.
*/
override in(): ASTNode|null {
return this.next();
}
/**
* Find the previous node in the pre order traversal.
*
* @returns The previous node, or null if the current node is not set or there
* is no previous value.
*/
override prev(): ASTNode|null {
const curNode = this.getCurNode();
if (!curNode) {
return null;
}
const newNode = this.getPreviousNode_(curNode, this.validNode_);
if (newNode) {
this.setCurNode(newNode);
}
return newNode;
}
/**
* For a basic cursor we only have the ability to go next and previous, so
* out will allow the user to get to the previous node in the pre order
* traversal.
*
* @returns The previous node, or null if the current node is not set or there
* is no previous value.
*/
override out(): ASTNode|null {
return this.prev();
}
/**
* Uses pre order traversal to navigate the Blockly AST. This will allow
* a user to easily navigate the entire Blockly AST without having to go in
* and out levels on the tree.
*
* @param node The current position in the AST.
* @param isValid A function true/false depending on whether the given node
* should be traversed.
* @returns The next node in the traversal.
*/
protected getNextNode_(
node: ASTNode|null, isValid: (p1: ASTNode|null) => boolean): ASTNode
|null {
if (!node) {
return null;
}
const newNode = node.in() || node.next();
if (isValid(newNode)) {
return newNode;
} else if (newNode) {
return this.getNextNode_(newNode, isValid);
}
const siblingOrParent = this.findSiblingOrParent_(node.out());
if (isValid(siblingOrParent)) {
return siblingOrParent;
} else if (siblingOrParent) {
return this.getNextNode_(siblingOrParent, isValid);
}
return null;
}
/**
* Reverses the pre order traversal in order to find the previous node. This
* will allow a user to easily navigate the entire Blockly AST without having
* to go in and out levels on the tree.
*
* @param node The current position in the AST.
* @param isValid A function true/false depending on whether the given node
* should be traversed.
* @returns The previous node in the traversal or null if no previous node
* exists.
*/
protected getPreviousNode_(
node: ASTNode|null, isValid: (p1: ASTNode|null) => boolean): ASTNode
|null {
if (!node) {
return null;
}
let newNode: ASTNode|null = node.prev();
if (newNode) {
newNode = this.getRightMostChild_(newNode);
} else {
newNode = node.out();
}
if (isValid(newNode)) {
return newNode;
} else if (newNode) {
return this.getPreviousNode_(newNode, isValid);
}
return null;
}
/**
* Decides what nodes to traverse and which ones to skip. Currently, it
* skips output, stack and workspace nodes.
*
* @param node The AST node to check whether it is valid.
* @returns True if the node should be visited, false otherwise.
*/
protected validNode_(node: ASTNode|null): boolean {
let isValid = false;
const type = node && node.getType();
if (type === ASTNode.types.OUTPUT || type === ASTNode.types.INPUT ||
type === ASTNode.types.FIELD || type === ASTNode.types.NEXT ||
type === ASTNode.types.PREVIOUS || type === ASTNode.types.WORKSPACE) {
isValid = true;
}
return isValid;
}
/**
* From the given node find either the next valid sibling or parent.
*
* @param node The current position in the AST.
* @returns The parent AST node or null if there are no valid parents.
*/
private findSiblingOrParent_(node: ASTNode|null): ASTNode|null {
if (!node) {
return null;
}
const nextNode = node.next();
if (nextNode) {
return nextNode;
}
return this.findSiblingOrParent_(node.out());
}
/**
* Get the right most child of a node.
*
* @param node The node to find the right most child of.
* @returns The right most child of the given node, or the node if no child
* exists.
*/
private getRightMostChild_(node: ASTNode|null): ASTNode|null {
if (!node!.in()) {
return node;
}
let newNode = node!.in();
while (newNode && newNode.next()) {
newNode = newNode.next();
}
return this.getRightMostChild_(newNode);
}
}
registry.register(
registry.Type.CURSOR, BasicCursor.registrationName, BasicCursor);