Index: src/gpu/gl/GrGLSLPrettyPrint.cpp |
diff --git a/src/gpu/gl/GrGLSLPrettyPrint.cpp b/src/gpu/gl/GrGLSLPrettyPrint.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..27f4b44e66b87258d7f9131a7fed789aefd5485e |
--- /dev/null |
+++ b/src/gpu/gl/GrGLSLPrettyPrint.cpp |
@@ -0,0 +1,173 @@ |
+/* |
+ * Copyright 2014 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+#include "gl/GrGLSLPrettyPrint.h" |
+ |
+namespace GrGLSLPrettyPrint { |
+ |
+class GLSLPrettyPrint { |
+public: |
+ GLSLPrettyPrint() {} |
+ |
+ SkString prettify(const SkString& input, bool countlines) { |
+ // setup pretty state |
+ fIndex = 0; |
+ fLength = input.size(); |
+ fInput = input; |
+ fCountlines = countlines; |
+ fTabs = 0; |
+ fLinecount = 1; |
+ fFreshline = true; |
+ |
+ int parensDepth = 0; |
+ // number 1st line |
+ this->lineNumbering(); |
+ while (fLength > fIndex) { |
+ /* the heart and soul of our prettification algorithm. The rules should hopefully be |
+ * self explanatory. For '#' and '//' tokens we parse until we reach a newline. |
+ * |
+ * For long style comments like this one, we search for the ending token. We also |
+ * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines |
+ * ourselves. This allows us to remain in control of line numbers, and matching tabs |
+ * Existing tabs in the input string are copied over too, but this will look funny |
+ * |
+ * '{' and '}' are handled in basically the same way. We add a newline if we aren't |
+ * on a fresh line, dirty the line, then add a second newline, ie braces are always |
+ * on their own lines indented properly. The one funkiness here is structs print with |
+ * the semicolon on its own line. Its not a problem for a glsl compiler though |
+ * |
+ * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala |
+ * in for loops. |
+ * |
+ * ';' means add a new line |
+ * |
+ * '\t' and '\n' are ignored in general parsing for backwards compatability with |
+ * existing shader code and we also have a special case for handling whitespace |
+ * at the beginning of fresh lines. |
+ * |
+ * Otherwise just add the new character to the pretty string, indenting if necessary. |
+ */ |
+ if (this->hasToken("#") || this->hasToken("//")) { |
+ this->parseUntilNewline(); |
+ } else if (this->hasToken("/*")) { |
+ this->parseUntil("*/"); |
+ } else if ('{' == fInput[fIndex]) { |
+ this->newline(); |
+ this->appendChar('{'); |
+ fTabs++; |
+ this->newline(); |
+ } else if ('}' == fInput[fIndex]) { |
+ fTabs--; |
+ this->newline(); |
+ this->appendChar('}'); |
+ this->newline(); |
+ } else if (this->hasToken(")")) { |
+ parensDepth--; |
+ } else if (this->hasToken("(")) { |
+ parensDepth++; |
+ } else if (!parensDepth && this->hasToken(";")) { |
+ this->newline(); |
+ } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || |
+ (fFreshline && ' ' == fInput[fIndex])) { |
+ fIndex++; |
+ } else { |
+ this->appendChar(input[fIndex]); |
+ } |
+ } |
+ return fPretty; |
+ } |
+private: |
+ void appendChar(char c) { |
+ this->tabString(); |
+ fPretty.appendf("%c", fInput[fIndex++]); |
+ fFreshline = false; |
+ } |
+ |
+ // hasToken automatically consumes the next token, if it is a match, and then tabs |
+ // if necessary, before inserting the token into the pretty string |
+ bool hasToken(const char* token) { |
+ size_t i = fIndex; |
+ for (size_t j = 0; token[j] && fLength > i; i++, j++) { |
+ if (token[j] != fInput[i]) { |
+ return false; |
+ } |
+ } |
+ this->tabString(); |
+ fIndex = i; |
+ fPretty.append(token); |
+ fFreshline = false; |
+ return true; |
+ } |
+ |
+ void parseUntilNewline() { |
+ while (fLength > fIndex) { |
+ if ('\n' == fInput[fIndex]) { |
+ fIndex++; |
+ this->newline(); |
+ break; |
+ } |
+ fPretty.appendf("%c", fInput[fIndex++]); |
+ } |
+ } |
+ |
+ // this code assumes it is not actually searching for a newline. If you need to search for a |
+ // newline, then use the function above. If you do search for a newline with this function |
+ // it will consume the entire string and the output will certainly not be prettified |
+ void parseUntil(const char* token) { |
+ while (fLength > fIndex) { |
+ // For embedded newlines, this code will make sure to embed the newline in the |
+ // pretty string, increase the linecount, and tab out the next line to the appropriate |
+ // place |
+ if ('\n' == fInput[fIndex]) { |
+ this->newline(); |
+ this->tabString(); |
+ fIndex++; |
+ } |
+ if (this->hasToken(token)) { |
+ break; |
+ } |
+ fFreshline = false; |
+ fPretty.appendf("%c", fInput[fIndex++]); |
+ } |
+ } |
+ |
+ // We only tab if on a newline, otherwise consider the line tabbed |
+ void tabString() { |
+ if (fFreshline) { |
+ for (int t = 0; t < fTabs; t++) { |
+ fPretty.append("\t"); |
+ } |
+ } |
+ } |
+ |
+ // newline is really a request to add a newline, if we are on a fresh line there is no reason |
+ // to add another newline |
+ void newline() { |
+ if (!fFreshline) { |
+ fFreshline = true; |
+ fPretty.append("\n"); |
+ this->lineNumbering(); |
+ } |
+ } |
+ |
+ void lineNumbering() { |
+ if (fCountlines) { |
+ fPretty.appendf("%4d\t", fLinecount++); |
+ } |
+ } |
+ |
+ bool fCountlines, fFreshline; |
+ int fTabs, fLinecount; |
+ size_t fIndex, fLength; |
+ SkString fInput, fPretty; |
+}; |
+ |
+SkString PrettyPrintGLSL(const SkString& input, bool countlines) { |
+ GLSLPrettyPrint pp; |
+ return pp.prettify(input, countlines); |
+} |
+ |
+} // end namespace |