matsuda
3/4/2010 - 8:57 AM

A textmate command to prettify JSON so that it looks more like the node repl's sys.inspect() output.

A textmate command to prettify JSON so that it looks more like the node repl's sys.inspect() output.

#!/usr/bin/env node

var sys = require("sys");

var indented = "";
(function (cb) {
  if (process.env.TM_SELECTED_TEXT) {
    return cb(process.env.TM_SELECTED_TEXT);
  }
  var buffer = "";
  process.stdio.open();
  process.stdio.addListener("data", function (data) { buffer += data });
  process.stdio.addListener("close", function () { cb(buffer) });
})(function (json) {
  
  indented = /(^[\t ]*)/.exec(json)[0] || "";
  try {
    var parsed = JSON.parse(json);
  } catch (ex) {
    var parsed = process.compile("("+json+")", "json");
  }
  
  // process.stdio.write(sys.inspect(parsed).replace(/(\{|,) ([\w\d_]+):/g, "$1 '$2':"));
  sys.puts(pretty(parsed, null, null, indented || "").replace(/[\t ]+(\n|$)/g, '$1'));
});

function pretty (obj, depth, name) {
  name = name || "";
  depth = depth || 0;
  
  if (typeof obj === "function") return "null";
  if (typeof obj === "object") {
    if (!obj) return "null";
    else if (obj instanceof Date) obj = JSON.stringify(obj);
    else if (
      obj instanceof Boolean ||
      obj instanceof String ||
      obj instanceof Number
    ) obj = obj.valueOf();
  }

  // Primitive types cannot have properties
  switch (typeof obj) {
    case "undefined":
    case "string":
    case "number":
    case "boolean":
      return JSON.stringify(obj);
  }
  var TAB = "\n" + indent(depth),
    out = [],
    inline, broken, brace;
  if (Array.isArray(obj)) {
    for (var i = 0, l = obj.length; i < l; i ++) {
      out.push((i ? ", " : "[ ") + pretty(obj[i], depth + 1));
    }
    brace = "]";
  } else {
    var sawFirst = false, out = [];
    for (var i in obj) {
      out.push((sawFirst ? ", " : "{ ") + JSON.stringify(i) + " : " + pretty(obj[i], depth + 1, i));
      sawFirst = true;
    }
    brace = "}";
  }
  inline = out.join("") + " " + brace;
  broken = (depth && name ? TAB : indented) + out.join(TAB) + TAB + brace;

  return (depth < 5 && (TAB + name + inline).length < 60) ? inline : broken;
}

function indent (depth) {
  var s = indented || "";
  while (depth --) s += "  ";
  return s;
}