Global Metrics

path: .metrics.mi.mi_visual_studio
old: 15.273038193572484
new: 15.268823451840923

path: .metrics.mi.mi_original
old: 26.116895311008943
new: 26.10968810264798

path: .metrics.mi.mi_sei
old: -15.39329756374608
new: -15.40369536750709

path: .metrics.halstead.purity_ratio
old: 0.94457799093314
new: 0.9432697111673048

path: .metrics.halstead.volume
old: 4917.349031901351
new: 4924.169210863765

path: .metrics.halstead.time
old: 11707.293189323273
new: 11766.47410270353

path: .metrics.halstead.effort
old: 210731.2774078189
new: 211796.53384866356

path: .metrics.halstead.length
old: 721.0
new: 722.0

path: .metrics.halstead.N2
old: 273.0
new: 274.0

path: .metrics.halstead.difficulty
old: 42.854651162790695
new: 43.01162790697674

path: .metrics.halstead.level
old: 0.02333469000135667
new: 0.023249526899161935

path: .metrics.halstead.bugs
old: 1.180406009345032
new: 1.1843806703256243

Spaces Data

Minimal test - lines (8, 264)

path: .spaces[0].metrics.mi.mi_original
old: 26.823600056454353
new: 26.81636275535567

path: .spaces[0].metrics.mi.mi_sei
old: -16.770188422731053
new: -16.780629641135533

path: .spaces[0].metrics.mi.mi_visual_studio
old: 15.686315822487924
new: 15.682083482664131

path: .spaces[0].metrics.halstead.level
old: 0.02296022960229602
new: 0.02287581699346405

path: .spaces[0].metrics.halstead.volume
old: 4878.390592039376
new: 4885.185007905727

path: .spaces[0].metrics.halstead.length
old: 718.0
new: 719.0

path: .spaces[0].metrics.halstead.N2
old: 271.0
new: 272.0

path: .spaces[0].metrics.halstead.bugs
old: 1.1868950186892473
new: 1.1909175080623615

path: .spaces[0].metrics.halstead.time
old: 11803.962950380992
new: 11864.020733485337

path: .spaces[0].metrics.halstead.purity_ratio
old: 0.9266526825512946
new: 0.9253638749260494

path: .spaces[0].metrics.halstead.difficulty
old: 43.55357142857143
new: 43.714285714285715

path: .spaces[0].metrics.halstead.effort
old: 212471.33310685784
new: 213552.37320273605

Code

define(function(require, exports, module) {
  // Dependencies
  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
  const { span } = require("devtools/client/shared/vendor/react-dom-factories");

  const {
    lengthBubble,
  } = require("devtools/client/shared/components/reps/shared/grip-length-bubble");
  const {
    interleave,
    getGripType,
    isGrip,
    wrapRender,
    ellipsisElement,
  } = require("devtools/client/shared/components/reps/reps/rep-utils");
  const {
    MODE,
  } = require("devtools/client/shared/components/reps/reps/constants");

  const {
    ModePropType,
  } = require("devtools/client/shared/components/reps/reps/array");
  const DEFAULT_TITLE = "Array";

  /**
   * Renders an array. The array is enclosed by left and right bracket
   * and the max number of rendered items depends on the current mode.
   */

  GripArray.propTypes = {
    object: PropTypes.object.isRequired,
    // @TODO Change this to Object.values when supported in Node's version of V8
    mode: ModePropType,
    provider: PropTypes.object,
    onDOMNodeMouseOver: PropTypes.func,
    onDOMNodeMouseOut: PropTypes.func,
    onInspectIconClick: PropTypes.func,
    shouldRenderTooltip: PropTypes.bool,
  };

  function GripArray(props) {
    const { object, mode = MODE.SHORT, shouldRenderTooltip } = props;

    let brackets;
    const needSpace = function(space) {
      return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
    };

    const config = {
      "data-link-actor-id": object.actor,
      className: "objectBox objectBox-array",
      title: shouldRenderTooltip ? "Array" : null,
    };

    const title = getTitle(props, object);

    if (mode === MODE.TINY) {
      const isEmpty = getLength(object) === 0;

      // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
      if (!isEmpty && object.class !== "Array") {
        return span(config, title);
      }

      brackets = needSpace(false);
      return span(
        config,
        title,
        span(
          {
            className: "arrayLeftBracket",
          },
          brackets.left
        ),
        isEmpty ? null : ellipsisElement,
        span(
          {
            className: "arrayRightBracket",
          },
          brackets.right
        )
      );
    }

    const max = maxLengthMap.get(mode);
    const items = arrayIterator(props, object, max);
    brackets = needSpace(items.length > 0);

    return span(
      config,
      title,
      span(
        {
          className: "arrayLeftBracket",
        },
        brackets.left
      ),
      ...interleave(items, ", "),
      span(
        {
          className: "arrayRightBracket",
        },
        brackets.right
      ),
      span({
        className: "arrayProperties",
        role: "group",
      })
    );
  }

  function getLength(grip) {
    if (!grip.preview) {
      return 0;
    }

    return grip.preview.length || grip.preview.childNodesLength || 0;
  }

  function getTitle(props, object) {
    const objectLength = getLength(object);
    const isEmpty = objectLength === 0;

    let title = props.title || object.class || DEFAULT_TITLE;

    const length = lengthBubble({
      object,
      mode: props.mode,
      maxLengthMap,
      getLength,
    });

    if (props.mode === MODE.TINY) {
      if (isEmpty) {
        if (object.class === DEFAULT_TITLE) {
          return null;
        }

        return span({ className: "objectTitle" }, `${title} `);
      }

      let trailingSpace;
      if (object.class === DEFAULT_TITLE) {
        title = null;
        trailingSpace = " ";
      }

      return span({ className: "objectTitle" }, title, length, trailingSpace);
    }

    return span({ className: "objectTitle" }, title, length, " ");
  }

  function getPreviewItems(grip) {
    if (!grip.preview) {
      return null;
    }

    return grip.preview.items || grip.preview.childNodes || [];
  }

  function arrayIterator(props, grip, max) {
    const { Rep } = require("devtools/client/shared/components/reps/reps/rep");

    let items = [];
    const gripLength = getLength(grip);

    if (!gripLength) {
      return items;
    }

    const previewItems = getPreviewItems(grip);
    const provider = props.provider;

    let emptySlots = 0;
    let foldedEmptySlots = 0;
    items = previewItems.reduce((res, itemGrip) => {
      if (res.length >= max) {
        return res;
      }

      let object;
      try {
        if (!provider && itemGrip === null) {
          emptySlots++;
          return res;
        }

        object = provider ? provider.getValue(itemGrip) : itemGrip;
      } catch (exc) {
        object = exc;
      }

      if (emptySlots > 0) {
        res.push(getEmptySlotsElement(emptySlots));
        foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
        emptySlots = 0;
      }

      if (res.length < max) {
        res.push(
          Rep({
            ...props,
            object,
            mode: MODE.TINY,
            // Do not propagate title to array items reps
            title: undefined,
          })
        );
      }

      return res;
    }, []);

    // Handle trailing empty slots if there are some.
    if (items.length < max && emptySlots > 0) {
      items.push(getEmptySlotsElement(emptySlots));
      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
    }

    const itemsShown = items.length + foldedEmptySlots;
    if (gripLength > itemsShown) {
      items.push(ellipsisElement);
    }

    return items;
  }

  function getEmptySlotsElement(number) {
    // TODO: Use l10N - See https://github.com/firefox-devtools/reps/issues/141
    return `<${number} empty slot${number > 1 ? "s" : ""}>`;
  }

  function supportsObject(grip, noGrip = false) {
    if (noGrip === true || !isGrip(grip)) {
      return false;
    }

    return (
      grip.preview &&
      (grip.preview.kind == "ArrayLike" ||
        getGripType(grip, noGrip) === "DocumentFragment")
    );
  }

  const maxLengthMap = new Map();
  maxLengthMap.set(MODE.SHORT, 3);
  maxLengthMap.set(MODE.LONG, 10);

  // Exports from this module
  module.exports = {
    rep: wrapRender(GripArray),
    supportsObject,
    maxLengthMap,
    getLength,
  };
});

Minimal test - lines (241, 251)

path: .spaces[0].spaces[6].metrics.halstead.bugs
old: 0.04772188926642685
new: 0.05066799285084565

path: .spaces[0].spaces[6].metrics.halstead.purity_ratio
old: 1.834586539016274
new: 1.7887218755408671

path: .spaces[0].spaces[6].metrics.halstead.level
old: 0.1
new: 0.09375

path: .spaces[0].spaces[6].metrics.halstead.effort
old: 1713.0037948837166
new: 1874.0554337189376

path: .spaces[0].spaces[6].metrics.halstead.length
old: 39.0
new: 40.0

path: .spaces[0].spaces[6].metrics.halstead.difficulty
old: 10.0
new: 10.666666666666666

path: .spaces[0].spaces[6].metrics.halstead.N2
old: 15.0
new: 16.0

path: .spaces[0].spaces[6].metrics.halstead.volume
old: 171.30037948837168
new: 175.69269691115042

path: .spaces[0].spaces[6].metrics.halstead.time
old: 95.1668774935398
new: 104.11419076216322

path: .spaces[0].spaces[6].metrics.mi.mi_original
old: 104.25831975317564
new: 104.12666715165732

path: .spaces[0].spaces[6].metrics.mi.mi_sei
old: 75.22120818433193
new: 75.03127362900135

path: .spaces[0].spaces[6].metrics.mi.mi_visual_studio
old: 60.969777633436046
new: 60.89278780798674

Code

  function supportsObject(grip, noGrip = false) {
    if (noGrip === true || !isGrip(grip)) {
      return false;
    }

    return (
      grip.preview &&
      (grip.preview.kind == "ArrayLike" ||
        getGripType(grip, noGrip) === "DocumentFragment")
    );
  }