All files / json-ot/types/ot-string util.ts

100% Statements 64/64
100% Branches 34/34
100% Functions 6/6
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94    3x 28525x 28523x 5533x 5533x   22990x 22990x 22990x   11266x 4588x 3609x 2153x 6678x 11266x     6661x 4678x 6661x     5063x 3672x 5063x         3x 14528x   7430x   4022x   3076x       3x 6035x   3172x   1342x   1521x       3x 3383x 3378x 3378x 3378x     3x 2162x 2162x 8666x 2162x 2162x                     3x 6742x   3075x     2115x 2115x     1552x 1552x 1552x        
import type {StringOp, StringOpComponent} from './types';
 
export const append = (op: StringOp, component: StringOpComponent): void => {
  if (!component) return;
  if (!op.length) {
    op.push(component);
    return;
  }
  const lastIndex = op.length - 1;
  const last = op[lastIndex];
  switch (typeof component) {
    case 'number': {
      if (typeof last === 'number') {
        if (component > 0 && last > 0) op[lastIndex] = last + component;
        else if (component < 0 && last < 0) op[lastIndex] = last + component;
        else op.push(component);
      } else op.push(component);
      break;
    }
    case 'string': {
      if (typeof last === 'string') op[lastIndex] = last + component;
      else op.push(component);
      break;
    }
    case 'object': {
      if (last instanceof Array) last[0] = last + component[0];
      else op.push(component);
      break;
    }
  }
};
 
export const componentLength = (component: StringOpComponent): number => {
  switch (typeof component) {
    case 'number':
      return Math.abs(component);
    case 'string':
      return component.length;
    default:
      return component[0].length;
  }
};
 
export const isDeleteComponent = (component: StringOpComponent): boolean => {
  switch (typeof component) {
    case 'number':
      return component < 0;
    case 'object':
      return true;
    default:
      return false;
  }
};
 
export const trim = (op: StringOp): void => {
  if (!op.length) return;
  const last = op[op.length - 1];
  const isLastRetain = typeof last === 'number' && last > 0;
  if (isLastRetain) op.pop();
};
 
export const normalize = (op: StringOp): StringOp => {
  const op2: StringOp = [];
  const length = op.length;
  for (let i = 0; i < length; i++) append(op2, op[i]);
  trim(op2);
  return op2;
};
 
/**
 * Extracts a full or a part of a component from an operation.
 *
 * @param component Component from which to extract a chunk.
 * @param offset Position within the component to start from.
 * @param maxLength Maximum length of the component to extract.
 * @returns Full or partial component at index `index` of operation `op`.
 */
export const chunk = (component: StringOpComponent, offset: number, maxLength: number): StringOpComponent => {
  switch (typeof component) {
    case 'number': {
      return component > 0 ? Math.min(component - offset, maxLength) : -Math.min(-component - offset, maxLength);
    }
    case 'string': {
      const end = Math.min(offset + maxLength, component.length);
      return component.substring(offset, end);
    }
    case 'object': {
      const str = component[0];
      const end = Math.min(offset + maxLength, str.length);
      return [str.substring(offset, end)];
    }
  }
};