Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR, wrote you some code for better assignment. #21

Open
id-ekaagr opened this issue Feb 16, 2024 · 0 comments
Open

PR, wrote you some code for better assignment. #21

id-ekaagr opened this issue Feb 16, 2024 · 0 comments

Comments

@id-ekaagr
Copy link

id-ekaagr commented Feb 16, 2024

Feel free to modify it as required.

const hasRequire=       /require\(/u;
const spaceMatcher=     /(?<n2>\s*)(?<n1>(?:\+|-|\*|\/|%|&|\^|\||<<|>>|\*\*|>>>)?=)/u;
const assignmentTokens= new Set ([
  `>>>=`,
  `<<=`,
  `>>=`,
  `**=`,
  `&&=`,
  `||=`,
  `??=`,
  `+=`,
  `-=`,
  `*=`,
  `/=`,
  `%=`,
  `&=`,
  `^=`,
  `|=`,
  `=`,
]);

module.exports= {
  meta: {
    fixable: `code`,
  },

  create: function (context) {
    const {
      options,
    } = context;
    const requiresOnly= Boolean (options) && options.length > 0 && options[0].requiresOnly;
    const sourceCode=   context.getSourceCode ();
    const groups=       [];
    let previousNode;
    return {
      "VariableDeclaration": function (node) {
        const source= sourceCode.getText (node);
        if (Boolean (requiresOnly) && ! hasRequire.test (source)) {
          return;
        }
        addNode (node, node);
      },
      "Program:exit":        checkAll,
      "ExpressionStatement": function (node) {
        const hasAssignmentExpression= Object.is (`AssignmentExpression`, node.expression.type);
        if (! hasAssignmentExpression) {
          return;
        }
        addNode (node, node.expression);
      },
    };
    function checkAll () {
      groups.forEach (check);
    }
    function isAssignmentExpression (node) {
      return Object.is (`AssignmentExpression`, node.type);
    }
    function addNode (groupNode, node) {
      if (shouldStartNewGroup (groupNode)) {
        groups.push ([
          node,
        ]);
      }
      else {
        getLast (groups).push (node);
      }
      previousNode= groupNode;
    }
    function shouldStartNewGroup (node) {
      // first line of all
      if (! previousNode) {
        return true;
      }
      // switching parent nodes
      if (! Object.is (previousNode.parent, node.parent)) {
        return true;
      }
      // If previous node was a for and included the declarations, new group
      if (Object.is (`ForStatement`, previousNode.parent.type) && previousNode.declarations) {
        return true;
      }
      // previous line was blank.
      const lineOfNode= sourceCode.getFirstToken (node).loc.start.line;
      const lineOfPrev= sourceCode.getLastToken (previousNode).loc.start.line;

      return ! Object.is (1, lineOfNode - lineOfPrev);
    }
    function check (group) {
      const maxPos= maxPosition (group);
      if (! areAligned (maxPos, group)) {
        context.report ({
          message: `This group of assignments is not aligned.`,
          loc:     {
            start: group[0].loc.start,
            end:   getLast (group).loc.end,
          },
          fix: fixer=>{
            const fixings= group.map (node=>{
              const tokens=          sourceCode.getTokens (node);
              const firstToken=      tokens[0];
              const assignmentToken= tokens.find (token=>assignmentTokens.has (token.value));
              const line=            sourceCode.getText (node);
              const lineIsAligned=   ! Object.is (` `, line.charAt (maxPos+1)) && Object.is (` `, line.charAt (maxPos));
              if (lineIsAligned || ! assignmentToken || isMultiline (firstToken, assignmentToken)) {
                return fixer.replaceText (node, line);
              }
              // source line may include spaces, we need to accomodate for that.
              const spacePrefix=    firstToken.loc.start.column;
              const startDelimiter= assignmentToken.loc.start.column - spacePrefix;
              const endDelimiter=   assignmentToken.loc.end.column - spacePrefix;
              const start=          line.slice (0, startDelimiter).replace (/\s+$/um, ``);
              const ending=         line.slice (endDelimiter).replace (/^\s+/um, ``);
              const spacesRequired= maxPos - start.length - assignmentToken.value.length + 1;
              const spaces=         ` `.repeat (spacesRequired);
              const fixedText=      `${start}${assignmentToken.value}${spaces}${ending}`;

              return fixer.replaceText (node, fixedText);
            });

            return fixings.filter (Boolean);
          },
        });
      }
    }
    function isMultiline (firstToken, assignmentToken) {
      return ! Object.is (firstToken.loc.start.line, assignmentToken.loc.start.line);
    }
    function findAssigment (node) {
      const prefix=   getPrefix (node);
      const source=   sourceCode.getText (node);
      const match=    source.slice (prefix).match (spaceMatcher);
      const position= match ?
        match.index + prefix + match[2].length :
        null;

      return position;
    }
    function getPrefix (node) {
      const nodeBefore= isAssignmentExpression (node) ?
        node.left:
        node.declarations.find (dcl=>Object.is (`VariableDeclarator`, dcl.type)).id;
      const prefix=     nodeBefore.loc.end.column - nodeBefore.loc.start.column;

      return prefix;
    }
    function areAligned (maxPos, nodes) {
      return nodes
        .filter (assignmentOnFirstLine)
        .map (node=>sourceCode.getText (node))
        .every (source=>{
          return ! Object.is (` `, source.charAt (maxPos+1)) &&
            Object.is (` `, source.charAt (maxPos+0));
        });
    }
    function maxPosition (nodes) {
      return nodes
        .filter (assignmentOnFirstLine)
        .map (findAssigment)
        .reduce ((previous, current)=>{
          return Math.max (previous, current);
        }, 0);
    }
    function assignmentOnFirstLine (node) {
      if (isAssignmentExpression (node)) {
        const onFirstLine= Object.is (node.right.loc.start.line, node.left.loc.start.line);

        return onFirstLine;
      }
      const source= sourceCode.getText (node);
      const lines=  source.split (`\n`);

      return lines[0].includes (`=`);
    }
    function getLast (ary) {
      return ary.at (- 1);
    }
  },
};

Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant