Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  lezer-utils.js

  Sprache: JAVA
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


"use strict";
// These are all the current lezer node types used in the source editor,
// Add more here as are needed
const nodeTypes = {
  AssignmentExpression: "AssignmentExpression",
  FunctionExpression: "FunctionExpression",
  FunctionDeclaration: "FunctionDeclaration",
  ArrowFunction: "ArrowFunction",
  MethodDeclaration: "MethodDeclaration",
  ClassDeclaration: "ClassDeclaration",
  ClassExpression: "ClassExpression",
  Property: "Property",
  PropertyDeclaration: "PropertyDeclaration",
  PropertyDefinition: "PropertyDefinition",
  PrivatePropertyDefinition: "PrivatePropertyDefinition",
  MemberExpression: "MemberExpression",
  VariableDeclaration: "VariableDeclaration",
  VariableDefinition: "VariableDefinition",
  VariableName: "VariableName",
  this"this",
  PropertyName: "PropertyName",
  Equals: "Equals",
  ParamList: "ParamList",
  Spread: "Spread",
  Number: "Number",
  Script: "Script",
  Block: "Block",
};

const functionsSet = new Set([
  nodeTypes.FunctionExpression,
  nodeTypes.FunctionDeclaration,
  nodeTypes.ArrowFunction,
  nodeTypes.MethodDeclaration,
]);

const nodeTypeSets = {
  functions: functionsSet,
  expressions: new Set([
    nodeTypes.MemberExpression,
    nodeTypes.VariableDefinition,
    nodeTypes.VariableName,
    nodeTypes.this,
    nodeTypes.PropertyName,
  ]),
  functionExpressions: new Set([
    nodeTypes.ArrowFunction,
    nodeTypes.FunctionExpression,
    nodeTypes.ParamList,
  ]),
  declarations: new Set([
    nodeTypes.MethodDeclaration,
    nodeTypes.PropertyDeclaration,
  ]),
  functionsDeclAndExpr: new Set([
    ...functionsSet,
    nodeTypes.Property,
    nodeTypes.PropertyDeclaration,
    nodeTypes.VariableDeclaration,
    nodeTypes.AssignmentExpression,
  ]),
  functionsVarDecl: new Set([
    ...functionsSet,
    // For anonymous functions we are using the variable name where the function is stored. See `getFunctionName`.
    nodeTypes.VariableDeclaration,
  ]),
  paramList: new Set([nodeTypes.ParamList]),
  variableDefinition: new Set([nodeTypes.VariableDefinition]),
  numberAndProperty: new Set([nodeTypes.PropertyDefinition, nodeTypes.Number]),
  memberExpression: new Set([nodeTypes.MemberExpression]),
  classes: new Set([nodeTypes.ClassDeclaration, nodeTypes.ClassExpression]),
  bindingReferences: new Set([
    nodeTypes.VariableDefinition,
    nodeTypes.VariableName,
    nodeTypes.PropertyName,
  ]),
  expressionProperty: new Set([nodeTypes.PropertyName]),
  variables: new Set([
    nodeTypes.VariableDefinition,
    nodeTypes.VariableDeclaration,
    nodeTypes.this,
  ]),
};

const ast = new Map();

/**
 * Checks if a node has children with any of the node types specified
 *
 * @param {object} node
 * @param {Set} types
 * @returns
 */

function hasChildNodeOfType(node, types) {
  let childNode = node.firstChild;
  while (childNode !== null) {
    if (types.has(childNode.name)) {
      return true;
    }
    childNode = childNode.nextSibling;
  }
  return false;
}

/**
 * Checks if a node has children with any of the node types specified
 *
 * @param {object} node
 * @param {Set} types
 * @returns
 */

function findChildNodeOfType(node, types) {
  let childNode = node.firstChild;
  while (childNode !== null) {
    if (types.has(childNode.name)) {
      return childNode;
    }
    childNode = childNode.nextSibling;
  }
  return null;
}

/**
 * Gets a cached tree or parses the the source content
 *
 * @param {object} parserLanguage - The language parser used to parse the source
 * @param {string} id - A unique identifier for the source
 * @param {string} content - The source text
 * @returns {Tree} - https://lezer.codemirror.net/docs/ref/#common.Tree
 */

function getTree(parserLanguage, id, content) {
  if (ast.has(id)) {
    return ast.get(id);
  }
  const tree = parserLanguage.parser.parse(content);
  ast.set(id, tree);
  return tree;
}

function clear() {
  ast.clear();
}

/**
 * Gets the node and the function name which immediately encloses the node (representing a location)
 *
 * @param {object} doc - The codemirror document used to retrive the part of content
 * @param {object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
 * @param {object} options
 * @param {boolean} options.includeAnonymousFunctions - if true, allow matching anonymous functions
 * @returns
 */

function getEnclosingFunction(
  doc,
  node,
  options = { includeAnonymousFunctions: false }
) {
  let parentNode = node.parent;
  while (parentNode !== null) {
    if (nodeTypeSets.functionsVarDecl.has(parentNode.name)) {
      // For anonymous functions, we use variable declarations, but we only care about variable declarations which are part of function expressions
      if (
        parentNode.name == nodeTypes.VariableDeclaration &&
        !hasChildNodeOfType(parentNode.node, nodeTypeSets.functionExpressions)
      ) {
        parentNode = parentNode.parent;
        continue;
      }
      const funcName = getFunctionName(doc, parentNode);
      if (funcName || options.includeAnonymousFunctions) {
        return {
          node: parentNode,
          funcName,
        };
      }
    }
    parentNode = parentNode.parent;
  }
  return null;
}

/**
 * Gets the parent scope node for the specified node.
 * If neither is found then we fallback to the script node for the source.
 *
 * @param {object} node
 * @param {string} scopeType - The scope type specifies what kind of scope node to look for.
 * The types are defined by the platform. See https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Environment.html#type
 * @returns {object | null} scope node or null if none is found
 */

function getParentScopeOfType(node, scopeType) {
  let parentNode = node.parent;
  let lastParentNode = parentNode;
  while (parentNode !== null) {
    if (scopeType == "block" || scopeType == "object") {
      if (parentNode.name == nodeTypes.Block) {
        return parentNode;
      }
    } else if (nodeTypeSets.functionsVarDecl.has(parentNode.name)) {
      if (
        parentNode.name == nodeTypes.VariableDeclaration &&
        !hasChildNodeOfType(parentNode.node, nodeTypeSets.functionExpressions)
      ) {
        parentNode = parentNode.parent;
        continue;
      }
      return parentNode;
    }
    lastParentNode = parentNode;
    parentNode = parentNode.parent;
  }
  // If no function node was found up to the root node
  if (lastParentNode?.name == nodeTypes.Script) {
    return lastParentNode;
  }
  return null;
}

/**
 * Gets the node at the specified location
 *
 * @param {object} doc - https://codemirror.net/docs/ref/#state.EditorState.doc
 * @param {object} tree - https://lezer.codemirror.net/docs/ref/#common.Tree
 * @param {object} location
 * @returns {object} node - https://lezer.codemirror.net/docs/ref/#common.SyntaxNodeRef
 */

function getTreeNodeAtLocation(doc, tree, location) {
  try {
    const line = doc.line(location.line);
    const pos = line.from + location.column;
    return tree.resolve(pos, 1);
  } catch (e) {
    // if the line is not found in the document doc.line() will throw
    console.warn(e.message);
  }
  return null;
}

/**
 * Converts Codemirror position to valid source location. Used only for CM6
 *
 * @param {object} doc - The Codemirror document used to retrive the part of content
 * @param {number} pos - Codemirror offset
 * @returns
 */

function positionToLocation(doc, pos) {
  if (pos == null) {
    return {
      line: null,
      column: null,
    };
  }
  const line = doc.lineAt(pos);
  return {
    line: line.number,
    column: pos - line.from,
  };
}

/**
 * Gets the name of the function if any exists, returns null
 * for anonymous functions.
 *
 * @param {object} doc - The codemirror document used to retrive the part of content
 * @param {object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
 * @returns {string | null}
 */

function getFunctionName(doc, node) {
  /**
   * Examples:
   *  - Gets `foo` in `class ESClass { foo(a, b) {}}`
   *  - Gets `bar` in `class ESClass { bar = function () {}}`
   *  - Gets `boo` in `class ESClass { boo = () => {}}`
   *  - Gets `#pfoo` in `class ESClass { #pfoo() => {}}`
   */

  if (
    node.name == nodeTypes.MethodDeclaration ||
    (node.name == nodeTypes.PropertyDeclaration &&
      hasChildNodeOfType(node, nodeTypeSets.functionExpressions))
  ) {
    const propDefNode = findChildNodeOfType(
      node,
      new Set([
        nodeTypes.PropertyDefinition,
        nodeTypes.PrivatePropertyDefinition,
      ])
    );

    if (!propDefNode) {
      return null;
    }
    return doc.sliceString(propDefNode.from, propDefNode.to);
  } else if (
    /**
     * Examples:
     *  - Gets `foo` in `let foo = function () {};`
     *  - Gets `bar` in `const bar = () => {}`
     */

    node.name == nodeTypes.VariableDeclaration &&
    hasChildNodeOfType(node, nodeTypeSets.functionExpressions)
  ) {
    const varDefNode = findChildNodeOfType(
      node,
      nodeTypeSets.variableDefinition
    );

    if (!varDefNode) {
      return null;
    }
    return doc.sliceString(varDefNode.from, varDefNode.to);
  } else if (
    /**
     * Examples:
     *  - Gets `Foo` in `function Foo() {} - FunctionDeclaration`
     *  - Gets `bar` in `function bar(a) {} - Functionexpression`
     */

    node.name == nodeTypes.FunctionDeclaration ||
    node.name == nodeTypes.FunctionExpression
  ) {
    const varDefNode = findChildNodeOfType(
      node,
      nodeTypeSets.variableDefinition
    );

    if (!varDefNode) {
      return null;
    }
    return doc.sliceString(varDefNode.from, varDefNode.to);
  } else if (
    /**
     * Examples:
     *  - Gets `foo` in `const a = { foo(a, ...b) {} }`
     *  - Gets `bar` in `const a = { bar: function () {} }`
     *  - Gets `bla` in `const a = { bla: () => {} }`
     *  - Gets `1234` in `const a = { 1234: () => {} }`
     */

    node.name == nodeTypes.Property &&
    hasChildNodeOfType(node, nodeTypeSets.functionExpressions)
  ) {
    const propDefNode = findChildNodeOfType(
      node,
      nodeTypeSets.numberAndProperty
    );

    if (!propDefNode) {
      return null;
    }
    return doc.sliceString(propDefNode.from, propDefNode.to);
  } else if (
    /**
     * Examples:
     *  - Gets `bar` in `const foo = {}; foo.bar = function() {}`
     *  - Gets `bla` in `const foo = {}; foo.bla = () => {}`
     */

    node.name == nodeTypes.AssignmentExpression &&
    hasChildNodeOfType(node, nodeTypeSets.functionExpressions)
  ) {
    const memExprDefNode = findChildNodeOfType(
      node,
      nodeTypeSets.memberExpression
    );

    if (!memExprDefNode) {
      return null;
    }
    // Get the rightmost part of the member expression i.e for a.b.c get c
    const exprParts = doc
      .sliceString(memExprDefNode.from, memExprDefNode.to)
      .split(".");
    return exprParts.at(-1);
  }

  return null;
}

/**
 * Gets the parameter names of the function as an array
 *
 * @param {object} doc - The codemirror document used to retrieve the part of content
 * @param {object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
 * @returns {Array}
 */

function getFunctionParameterNames(doc, node) {
  // Find the parameter list node

  let exprNode = node;

  if (
    // Example: Gets `(a)` in `const foo = {}; foo.bar = function(a) {}`
    node.name == nodeTypes.AssignmentExpression ||
    // Example: Gets `(a, b)` in `let foo = function (a, b) {};`
    node.name == nodeTypes.VariableDeclaration ||
    // Example: Gets `(x, y)` in `class ESClass { bar = function (x, y) {}}`
    node.name == nodeTypes.PropertyDeclaration ||
    // Example: Gets `(foo, ...bar)` in `const a = { foo: (foo, ...bar) {}}`
    (node.name == nodeTypes.Property &&
      !hasChildNodeOfType(node, nodeTypeSets.paramList))
  ) {
    exprNode = findChildNodeOfType(node, nodeTypeSets.functionExpressions);
  }

  /**
   * Others
   *  Function Declarations - Gets `(x, y)` in `function Foo(x, y) {}`
   *  Method Declarations - Gets `(a, b)` in `class ESClass { foo(a, b) {}}`
   */

  const paramListNode = findChildNodeOfType(exprNode, nodeTypeSets.paramList);
  if (paramListNode == null) {
    return [];
  }

  const names = [];
  let currNode = paramListNode.firstChild; // "("
  // Get all the parameter names
  while (currNode !== null && currNode.name !== ")") {
    if (currNode.name == nodeTypes.VariableDefinition) {
      // ignore spread operators i.e foo(...x)
      if (currNode.prevSibling?.name !== nodeTypes.Spread) {
        names.push(doc.sliceString(currNode.from, currNode.to));
      }
    }
    currNode = currNode.nextSibling;
  }
  return names;
}

function getFunctionClass(doc, node) {
  /**
   * Examples (Class Methods and Properties):
   *  Gets `ESClass` in `class ESClass { foo(a, b) {}}`
   *  Gets `ESClass` in `class ESClass { bar = function () {}}`
   */

  if (!nodeTypeSets.declarations.has(node.name)) {
    return null;
  }
  return doc.sliceString(
    node.parent.prevSibling.from,
    node.parent.prevSibling.to
  );
}

/**
 * Gets the meta data for member expression nodes
 *
 * @param {object} doc - The codemirror document used to retrieve the part of content
 * @param {object} node - The parser syntax node https://lezer.codemirror.net/docs/ref/#common.SyntaxNode
 * @returns
 */

function getMetaBindings(doc, node) {
  if (!node || node.name !== nodeTypes.MemberExpression) {
    return null;
  }

  const memExpr = doc.sliceString(node.from, node.to).split(".");
  return {
    type: "member",
    start: positionToLocation(doc, node.from),
    end: positionToLocation(doc, node.to),
    property: memExpr.at(-1),
    parent: getMetaBindings(doc, node.parent),
  };
}

/**
 * Walk the syntax tree of the langauge provided
 *
 * @param {object}   view - Codemirror view (https://codemirror.net/docs/ref/#view)
 * @param {object}   language - Codemirror Language (https://codemirror.net/docs/ref/#language)
 * @param {object}   options
 *        {Boolean}  options.forceParseTo - Force parsing the document up to a certain point
 *        {Function} options.enterVisitor - A function that is called when a node is entered
 *        {Set}      options.filterSet - A set of node types which should be visited, all others should be ignored
 *        {Number}   options.walkFrom - Determine the location in the AST where the iteration of the syntax tree should start
 *        {Number}   options.walkTo - Determine the location in the AST where the iteration of the syntax tree should end
 */

async function walkTree(view, language, options) {
  const { forceParsing, syntaxTree } = language;
  if (options.forceParseTo) {
    // Force parsing the source, up to the end of the current viewport,
    // Also increasing the timeout threshold so we make sure
    // all required content is parsed (this is mostly needed for larger sources).
    await forceParsing(view, options.forceParseTo, 10000);
  }
  await syntaxTree(view.state).iterate({
    enter: node => {
      if (options.filterSet?.has(node.name)) {
        options.enterVisitor(node);
      }
    },
    from: options.walkFrom,
    to: options.walkTo,
  });
}

/**
 * This enables walking a specific part of the syntax tree using the cursor
 * provided by the node (which is the parent)
 *
 * @param {object} cursor - https://lezer.codemirror.net/docs/ref/#common.TreeCursor
 * @param {object} options
 *        {Function} options.enterVisitor - A function that is called when a node is entered
 *        {Set}      options.filterSet - A set of node types which should be visited, all others should be ignored
 */

async function walkCursor(cursor, options) {
  await cursor.iterate(node => {
    if (options.filterSet?.has(node.name)) {
      options.enterVisitor(node);
    }
  });
}

/**
 * Merge variables, arguments and child properties of member expressions
 * into a unique "bindings" objects where arguments overrides variables.
 *
 * @param {object} scopeBindings
 * @returns {object} bindings
 */

function getScopeBindings(scopeBindings) {
  const bindings = { ...scopeBindings.variables };
  scopeBindings.arguments.forEach(argument => {
    Object.keys(argument).forEach(key => {
      bindings[key] = argument[key];
    });
  });
  // Find and add child properties of member expressions as bindings
  for (const v in scopeBindings.variables) {
    const ownProps = scopeBindings.variables[v]?.value?.preview?.ownProperties;
    if (ownProps) {
      Object.keys(ownProps).forEach(k => {
        bindings[k] = ownProps[k];
      });
    }
  }
  return bindings;
}

module.exports = {
  getFunctionName,
  getFunctionParameterNames,
  getFunctionClass,
  getEnclosingFunction,
  getTreeNodeAtLocation,
  getMetaBindings,
  nodeTypes,
  nodeTypeSets,
  walkTree,
  getTree,
  clear,
  walkCursor,
  positionToLocation,
  getParentScopeOfType,
  getScopeBindings,
};

Messung V0.5 in Prozent
C=75 H=93 G=84

¤ Dauer der Verarbeitung: 0.11 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002