Revert "Revert "Support single line objects and arrays""

This reverts commit a5a69cfb480b99747ddc272149d35c6302abf1bf.

Bug: skia:
Change-Id: I08475d96255b9df13e5c86e1ef9c7f4739e51459
Reviewed-on: https://skia-review.googlesource.com/33202
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 00475e6..a418452 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -193,7 +193,7 @@
 
     for (size_t i = 1; i < kGrPixelConfigCnt; ++i) {
         GrPixelConfig config = static_cast<GrPixelConfig>(i);
-        writer->beginObject();
+        writer->beginObject(nullptr, false);
         writer->appendString("name", pixel_config_name(config));
         writer->appendBool("renderable", this->isConfigRenderable(config, false));
         writer->appendBool("renderableMSAA", this->isConfigRenderable(config, true));
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index 03495e9..f73d218 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -117,7 +117,7 @@
         for (int p = 0; p < kGrSLPrecisionCount; ++p) {
             if (fFloatPrecisions[s][p].supported()) {
                 GrSLPrecision precision = static_cast<GrSLPrecision>(p);
-                writer->beginObject();
+                writer->beginObject(nullptr, false);
                 writer->appendString("precision", precision_to_string(precision));
                 writer->appendS32("log_low", fFloatPrecisions[s][p].fLogRangeLow);
                 writer->appendS32("log_high", fFloatPrecisions[s][p].fLogRangeHigh);
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index b90eefb..6e7a949 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1258,7 +1258,7 @@
     writer->beginArray("Stencil Formats");
 
     for (int i = 0; i < fStencilFormats.count(); ++i) {
-        writer->beginObject();
+        writer->beginObject(nullptr, false);
         writer->appendS32("stencil bits", fStencilFormats[i].fStencilBits);
         writer->appendS32("total bits", fStencilFormats[i].fTotalBits);
         writer->endObject();
@@ -1339,7 +1339,7 @@
     writer->beginArray("configs");
 
     for (int i = 0; i < kGrPixelConfigCnt; ++i) {
-        writer->beginObject();
+        writer->beginObject(nullptr, false);
         writer->appendHexU32("flags", fConfigTable[i].fFlags);
         writer->appendHexU32("b_internal", fConfigTable[i].fFormats.fBaseInternalFormat);
         writer->appendHexU32("s_internal", fConfigTable[i].fFormats.fSizedInternalFormat);
diff --git a/src/utils/SkJSONWriter.h b/src/utils/SkJSONWriter.h
index 65f9990..6cad6b4 100644
--- a/src/utils/SkJSONWriter.h
+++ b/src/utils/SkJSONWriter.h
@@ -53,12 +53,14 @@
             , fMode(mode)
             , fState(State::kStart) {
         fScopeStack.push_back(Scope::kNone);
+        fNewlineStack.push_back(true);
     }
 
     ~SkJSONWriter() {
         this->flush();
         delete[] fBlock;
         SkASSERT(fScopeStack.count() == 1);
+        SkASSERT(fNewlineStack.count() == 1);
     }
 
     /**
@@ -85,7 +87,7 @@
         if (State::kObjectValue == fState) {
             this->write(",", 1);
         }
-        this->newline();
+        this->separator(this->multiline());
         this->write("\"", 1);
         this->write(name, strlen(name));
         this->write("\":", 2);
@@ -95,12 +97,17 @@
     /**
      *  Adds a new object. A name must be supplied when called between beginObject() and
      *  endObject(). Calls to beginObject() must be balanced by corresponding calls to endObject().
+     *  By default, objects are written out with one named value per line (when in kPretty mode).
+     *  This can be overridden for a particular object by passing false for multiline, this will
+     *  keep the entire object on a single line. This can help with readability in some situations.
+     *  In kFast mode, this parameter is ignored.
      */
-    void beginObject(const char* name = nullptr) {
+    void beginObject(const char* name = nullptr, bool multiline = true) {
         this->appendName(name);
         this->beginValue(true);
         this->write("{", 1);
         fScopeStack.push_back(Scope::kObject);
+        fNewlineStack.push_back(multiline);
         fState = State::kObjectBegin;
     }
 
@@ -111,9 +118,10 @@
         SkASSERT(Scope::kObject == this->scope());
         SkASSERT(State::kObjectBegin == fState || State::kObjectValue == fState);
         bool emptyObject = State::kObjectBegin == fState;
+        bool wasMultiline = this->multiline();
         this->popScope();
         if (!emptyObject) {
-            this->newline();
+            this->separator(wasMultiline);
         }
         this->write("}", 1);
     }
@@ -121,12 +129,17 @@
     /**
      *  Adds a new array. A name must be supplied when called between beginObject() and
      *  endObject(). Calls to beginArray() must be balanced by corresponding calls to endArray().
+     *  By default, arrays are written out with one value per line (when in kPretty mode).
+     *  This can be overridden for a particular array by passing false for multiline, this will
+     *  keep the entire array on a single line. This can help with readability in some situations.
+     *  In kFast mode, this parameter is ignored.
      */
-    void beginArray(const char* name = nullptr) {
+    void beginArray(const char* name = nullptr, bool multiline = true) {
         this->appendName(name);
         this->beginValue(true);
         this->write("[", 1);
         fScopeStack.push_back(Scope::kArray);
+        fNewlineStack.push_back(multiline);
         fState = State::kArrayBegin;
     }
 
@@ -137,9 +150,10 @@
         SkASSERT(Scope::kArray == this->scope());
         SkASSERT(State::kArrayBegin == fState || State::kArrayValue == fState);
         bool emptyArray = State::kArrayBegin == fState;
+        bool wasMultiline = this->multiline();
         this->popScope();
         if (!emptyArray) {
-            this->newline();
+            this->separator(wasMultiline);
         }
         this->write("]", 1);
     }
@@ -245,7 +259,7 @@
             this->write(",", 1);
         }
         if (Scope::kArray == this->scope()) {
-            this->newline();
+            this->separator(this->multiline());
         } else if (Scope::kObject == this->scope() && Mode::kPretty == fMode) {
             this->write(" ", 1);
         }
@@ -256,11 +270,15 @@
         }
     }
 
-    void newline() {
+    void separator(bool multiline) {
         if (Mode::kPretty == fMode) {
-            this->write("\n", 1);
-            for (int i = 0; i < fScopeStack.count() - 1; ++i) {
-                this->write("   ", 3);
+            if (multiline) {
+                this->write("\n", 1);
+                for (int i = 0; i < fScopeStack.count() - 1; ++i) {
+                    this->write("   ", 3);
+                }
+            } else {
+                this->write(" ", 1);
             }
         }
     }
@@ -284,8 +302,14 @@
         return fScopeStack.back();
     }
 
+    bool multiline() const {
+        SkASSERT(!fNewlineStack.empty());
+        return fNewlineStack.back();
+    }
+
     void popScope() {
         fScopeStack.pop_back();
+        fNewlineStack.pop_back();
         switch (this->scope()) {
             case Scope::kNone:
                 fState = State::kEnd;
@@ -310,6 +334,7 @@
     Mode fMode;
     State fState;
     SkSTArray<16, Scope, true> fScopeStack;
+    SkSTArray<16, bool, true> fNewlineStack;
 };
 
 #endif