remirror/remirror

View on GitHub
packages/remirror__extension-count/src/count-utils.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import type { EditorState, ProsemirrorNode } from '@remirror/core';
import { findMatches } from '@remirror/core';

// Consider anything not whitespace a word. (Microsoft Word behaviour)
export const WORDS_REGEX = /\S+/g;

export function getTextLength(node: ProsemirrorNode): number {
  if (node.type.isTextblock) {
    // Count the linebreak at the end of a block node
    return 1;
  }

  if (node.type.isText) {
    return node.textBetween(0, node.nodeSize).length;
  }

  return 0;
}

export function getCharacterExceededPosition({ doc }: EditorState, maxCharacters: number): number {
  let count = 0;
  let foundPos = 0;

  doc.nodesBetween(0, doc.nodeSize - 2, (node, pos) => {
    if (foundPos > 0) {
      // Already found the position - return early
      return false;
    }

    const nodeTextLength = getTextLength(node);

    if (count + nodeTextLength > maxCharacters) {
      foundPos = pos + 1 + (maxCharacters - count);
      return false;
    }

    count += nodeTextLength;
    return true;
  });

  return foundPos;
}

export function getWordExceededPosition({ doc }: EditorState, maxWords: number): number {
  let count = 0;
  let foundPos = 0;

  doc.nodesBetween(0, doc.nodeSize - 2, (node, pos) => {
    if (foundPos > 0) {
      // Already found the position - return early
      return false;
    }

    if (!node.type.isText) {
      // Descend into node
      return true;
    }

    const text = node.textBetween(0, node.nodeSize);
    const matches = findMatches(text, WORDS_REGEX);

    if (count + matches.length > maxWords) {
      const index = maxWords - count;
      const word = matches[index];
      foundPos = pos + (word?.index ?? 0);
      return false;
    }

    count += matches.length;
    return true;
  });

  return foundPos;
}