1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 #include "GrSKSLPrettyPrint.h" 8 #include "SkSLString.h" 9 10 namespace GrSKSLPrettyPrint { 11 12 class GLSLPrettyPrint { 13 public: 14 GLSLPrettyPrint() {} 15 16 SkSL::String prettify(const char** strings, int* lengths, int count, bool countlines) { 17 fCountlines = countlines; 18 fTabs = 0; 19 fLinecount = 1; 20 fFreshline = true; 21 22 // If a string breaks while in the middle 'parse until' we need to continue parsing on the 23 // next string 24 fInParseUntilNewline = false; 25 fInParseUntil = false; 26 27 int parensDepth = 0; 28 29 // number 1st line 30 this->lineNumbering(); 31 for (int i = 0; i < count; i++) { 32 // setup pretty state 33 fIndex = 0; 34 fLength = lengths[i]; 35 fInput = strings[i]; 36 37 while (fLength > fIndex) { 38 /* the heart and soul of our prettification algorithm. The rules should hopefully 39 * be self explanatory. For '#' and '//' tokens we parse until we reach a newline. 40 * 41 * For long style comments like this one, we search for the ending token. We also 42 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines 43 * ourselves. This allows us to remain in control of line numbers, and matching 44 * tabs Existing tabs in the input string are copied over too, but this will look 45 * funny 46 * 47 * '{' and '}' are handled in basically the same way. We add a newline if we aren't 48 * on a fresh line, dirty the line, then add a second newline, ie braces are always 49 * on their own lines indented properly. The one funkiness here is structs print 50 * with the semicolon on its own line. Its not a problem for a glsl compiler though 51 * 52 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala 53 * in for loops. 54 * 55 * ';' means add a new line 56 * 57 * '\t' and '\n' are ignored in general parsing for backwards compatability with 58 * existing shader code and we also have a special case for handling whitespace 59 * at the beginning of fresh lines. 60 * 61 * Otherwise just add the new character to the pretty string, indenting if 62 * necessary. 63 */ 64 if (fInParseUntilNewline) { 65 this->parseUntilNewline(); 66 } else if (fInParseUntil) { 67 this->parseUntil(fInParseUntilToken); 68 } else if (this->hasToken("#") || this->hasToken("//")) { 69 this->parseUntilNewline(); 70 } else if (this->hasToken("/*")) { 71 this->parseUntil("*/"); 72 } else if ('{' == fInput[fIndex]) { 73 this->newline(); 74 this->appendChar('{'); 75 fTabs++; 76 this->newline(); 77 } else if ('}' == fInput[fIndex]) { 78 fTabs--; 79 this->newline(); 80 this->appendChar('}'); 81 this->newline(); 82 } else if (this->hasToken(")")) { 83 parensDepth--; 84 } else if (this->hasToken("(")) { 85 parensDepth++; 86 } else if (!parensDepth && this->hasToken(";")) { 87 this->newline(); 88 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] || 89 (fFreshline && ' ' == fInput[fIndex])) { 90 fIndex++; 91 } else { 92 this->appendChar(fInput[fIndex]); 93 } 94 } 95 } 96 return fPretty; 97 } 98 99 private: 100 void appendChar(char c) { 101 this->tabString(); 102 fPretty.appendf("%c", fInput[fIndex++]); 103 fFreshline = false; 104 } 105 106 // hasToken automatically consumes the next token, if it is a match, and then tabs 107 // if necessary, before inserting the token into the pretty string 108 bool hasToken(const char* token) { 109 size_t i = fIndex; 110 for (size_t j = 0; token[j] && fLength > i; i++, j++) { 111 if (token[j] != fInput[i]) { 112 return false; 113 } 114 } 115 this->tabString(); 116 fIndex = i; 117 fPretty.append(token); 118 fFreshline = false; 119 return true; 120 } 121 122 void parseUntilNewline() { 123 while (fLength > fIndex) { 124 if ('\n' == fInput[fIndex]) { 125 fIndex++; 126 this->newline(); 127 fInParseUntilNewline = false; 128 break; 129 } 130 fPretty.appendf("%c", fInput[fIndex++]); 131 fInParseUntilNewline = true; 132 } 133 } 134 135 // this code assumes it is not actually searching for a newline. If you need to search for a 136 // newline, then use the function above. If you do search for a newline with this function 137 // it will consume the entire string and the output will certainly not be prettified 138 void parseUntil(const char* token) { 139 while (fLength > fIndex) { 140 // For embedded newlines, this code will make sure to embed the newline in the 141 // pretty string, increase the linecount, and tab out the next line to the appropriate 142 // place 143 if ('\n' == fInput[fIndex]) { 144 this->newline(); 145 this->tabString(); 146 fIndex++; 147 } 148 if (this->hasToken(token)) { 149 fInParseUntil = false; 150 break; 151 } 152 fFreshline = false; 153 fPretty.appendf("%c", fInput[fIndex++]); 154 fInParseUntil = true; 155 fInParseUntilToken = token; 156 } 157 } 158 159 // We only tab if on a newline, otherwise consider the line tabbed 160 void tabString() { 161 if (fFreshline) { 162 for (int t = 0; t < fTabs; t++) { 163 fPretty.append("\t"); 164 } 165 } 166 } 167 168 // newline is really a request to add a newline, if we are on a fresh line there is no reason 169 // to add another newline 170 void newline() { 171 if (!fFreshline) { 172 fFreshline = true; 173 fPretty.append("\n"); 174 this->lineNumbering(); 175 } 176 } 177 178 void lineNumbering() { 179 if (fCountlines) { 180 fPretty.appendf("%4d\t", fLinecount++); 181 } 182 } 183 184 bool fCountlines, fFreshline; 185 int fTabs, fLinecount; 186 size_t fIndex, fLength; 187 const char* fInput; 188 SkSL::String fPretty; 189 190 // Some helpers for parseUntil when we go over a string length 191 bool fInParseUntilNewline; 192 bool fInParseUntil; 193 const char* fInParseUntilToken; 194 }; 195 196 SkSL::String PrettyPrint(const char** strings, int* lengths, int count, bool countlines) { 197 GLSLPrettyPrint pp; 198 return pp.prettify(strings, lengths, count, countlines); 199 } 200 201 } // namespace GrSKSLPrettyPrint 202