Files
blockly/core/keyboard_nav/basic_cursor.ts
T
Christopher Allen b0475b0c68 chore: Fix whitespace (#6243)
* fix: Remove spurious blank lines

  Remove extraneous blank lines introduced by deletion of
  'use strict'; pragmas.

  Also fix the location of the goog.declareModuleId call in
  core/utils/array.ts.

* fix: Add missing double-blank-line before body of modules

  Our convention is to have two blank lines between the imports (or
  module ID, if there are no imports) and the beginning of the body
  of the module.  Enforce this.

* fix: one addition format error for PR #6243
2022-06-24 19:33:39 +01:00

215 lines
6.1 KiB
TypeScript

/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview The class representing a basic cursor.
* Used to demo switching between different cursors.
*/
/**
* 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.
* @return 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.
* @return 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.
* @return 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.
* @return 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.
* @return 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());
// AnyDuringMigration because: Argument of type 'ASTNode | null' is not
// assignable to parameter of type 'ASTNode'.
if (isValid(siblingOrParent as AnyDuringMigration)) {
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.
* @return 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.
* @return 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.
* @return 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.
* @return 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);