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 28794x 28792x 5529x 5529x   23263x 23263x 23263x   11297x 4580x 3568x 2196x 6717x 11297x     6679x 4801x 6679x     5287x 3853x 5287x         3x 14384x   7570x   3706x   3108x       3x 6040x   3223x   1368x   1449x       3x 3382x 3375x 3375x 3375x     3x 2161x 2161x 8787x 2161x 2161x                     3x 6693x   3164x     1954x 1954x     1575x 1575x 1575x        
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)];
    }
  }
};