blob: f00682f6017d4ba26a9a6a9d287894f31c787ab5 [file] [log] [blame]
joshualittbab82ed2014-08-08 09:41:42 -07001/*
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 */
Brian Salomone334c592017-05-15 11:00:58 -04007#include "GrSKSLPrettyPrint.h"
Brian Osman93ba0a42017-08-14 14:48:10 -04008#include "SkSLString.h"
joshualittbab82ed2014-08-08 09:41:42 -07009
Brian Salomone334c592017-05-15 11:00:58 -040010namespace GrSKSLPrettyPrint {
joshualittbab82ed2014-08-08 09:41:42 -070011
12class GLSLPrettyPrint {
13public:
14 GLSLPrettyPrint() {}
15
Brian Osman6c431d52019-04-15 16:31:54 -040016 SkSL::String prettify(const SkSL::String& string) {
joshualittbab82ed2014-08-08 09:41:42 -070017 fTabs = 0;
joshualittbab82ed2014-08-08 09:41:42 -070018 fFreshline = true;
19
joshualitt43466a12015-02-13 17:18:27 -080020 // If a string breaks while in the middle 'parse until' we need to continue parsing on the
21 // next string
22 fInParseUntilNewline = false;
23 fInParseUntil = false;
24
joshualittbab82ed2014-08-08 09:41:42 -070025 int parensDepth = 0;
joshualitt43466a12015-02-13 17:18:27 -080026
Brian Osman6c431d52019-04-15 16:31:54 -040027 // setup pretty state
28 fIndex = 0;
29 fLength = string.length();
30 fInput = string.c_str();
joshualitt43466a12015-02-13 17:18:27 -080031
Brian Osman6c431d52019-04-15 16:31:54 -040032 while (fLength > fIndex) {
33 /* the heart and soul of our prettification algorithm. The rules should hopefully
34 * be self explanatory. For '#' and '//' tokens we parse until we reach a newline.
35 *
36 * For long style comments like this one, we search for the ending token. We also
37 * preserve whitespace in these comments WITH THE CAVEAT that we do the newlines
38 * ourselves. This allows us to remain in control of line numbers, and matching
39 * tabs Existing tabs in the input string are copied over too, but this will look
40 * funny
41 *
42 * '{' and '}' are handled in basically the same way. We add a newline if we aren't
43 * on a fresh line, dirty the line, then add a second newline, ie braces are always
44 * on their own lines indented properly. The one funkiness here is structs print
45 * with the semicolon on its own line. Its not a problem for a glsl compiler though
46 *
47 * '(' and ')' are basically ignored, except as a sign we need to ignore ';' ala
48 * in for loops.
49 *
50 * ';' means add a new line
51 *
52 * '\t' and '\n' are ignored in general parsing for backwards compatability with
53 * existing shader code and we also have a special case for handling whitespace
54 * at the beginning of fresh lines.
55 *
56 * Otherwise just add the new character to the pretty string, indenting if
57 * necessary.
58 */
59 if (fInParseUntilNewline) {
60 this->parseUntilNewline();
61 } else if (fInParseUntil) {
62 this->parseUntil(fInParseUntilToken);
63 } else if (this->hasToken("#") || this->hasToken("//")) {
64 this->parseUntilNewline();
65 } else if (this->hasToken("/*")) {
66 this->parseUntil("*/");
67 } else if ('{' == fInput[fIndex]) {
68 this->newline();
69 this->appendChar('{');
70 fTabs++;
71 this->newline();
72 } else if ('}' == fInput[fIndex]) {
73 fTabs--;
74 this->newline();
75 this->appendChar('}');
76 this->newline();
77 } else if (this->hasToken(")")) {
78 parensDepth--;
79 } else if (this->hasToken("(")) {
80 parensDepth++;
81 } else if (!parensDepth && this->hasToken(";")) {
82 this->newline();
83 } else if ('\t' == fInput[fIndex] || '\n' == fInput[fIndex] ||
84 (fFreshline && ' ' == fInput[fIndex])) {
85 fIndex++;
86 } else {
87 this->appendChar(fInput[fIndex]);
joshualittbab82ed2014-08-08 09:41:42 -070088 }
89 }
Brian Osman6c431d52019-04-15 16:31:54 -040090
joshualittbab82ed2014-08-08 09:41:42 -070091 return fPretty;
92 }
Brian Salomone334c592017-05-15 11:00:58 -040093
joshualittbab82ed2014-08-08 09:41:42 -070094private:
95 void appendChar(char c) {
96 this->tabString();
97 fPretty.appendf("%c", fInput[fIndex++]);
98 fFreshline = false;
99 }
100
101 // hasToken automatically consumes the next token, if it is a match, and then tabs
102 // if necessary, before inserting the token into the pretty string
103 bool hasToken(const char* token) {
104 size_t i = fIndex;
105 for (size_t j = 0; token[j] && fLength > i; i++, j++) {
106 if (token[j] != fInput[i]) {
107 return false;
108 }
109 }
110 this->tabString();
111 fIndex = i;
112 fPretty.append(token);
113 fFreshline = false;
114 return true;
115 }
116
117 void parseUntilNewline() {
118 while (fLength > fIndex) {
119 if ('\n' == fInput[fIndex]) {
120 fIndex++;
121 this->newline();
joshualitt43466a12015-02-13 17:18:27 -0800122 fInParseUntilNewline = false;
joshualittbab82ed2014-08-08 09:41:42 -0700123 break;
124 }
125 fPretty.appendf("%c", fInput[fIndex++]);
joshualitt43466a12015-02-13 17:18:27 -0800126 fInParseUntilNewline = true;
joshualittbab82ed2014-08-08 09:41:42 -0700127 }
128 }
129
130 // this code assumes it is not actually searching for a newline. If you need to search for a
131 // newline, then use the function above. If you do search for a newline with this function
132 // it will consume the entire string and the output will certainly not be prettified
133 void parseUntil(const char* token) {
134 while (fLength > fIndex) {
135 // For embedded newlines, this code will make sure to embed the newline in the
136 // pretty string, increase the linecount, and tab out the next line to the appropriate
137 // place
138 if ('\n' == fInput[fIndex]) {
139 this->newline();
140 this->tabString();
141 fIndex++;
142 }
143 if (this->hasToken(token)) {
joshualitt43466a12015-02-13 17:18:27 -0800144 fInParseUntil = false;
joshualittbab82ed2014-08-08 09:41:42 -0700145 break;
146 }
147 fFreshline = false;
148 fPretty.appendf("%c", fInput[fIndex++]);
joshualitt43466a12015-02-13 17:18:27 -0800149 fInParseUntil = true;
150 fInParseUntilToken = token;
joshualittbab82ed2014-08-08 09:41:42 -0700151 }
152 }
153
154 // We only tab if on a newline, otherwise consider the line tabbed
155 void tabString() {
156 if (fFreshline) {
157 for (int t = 0; t < fTabs; t++) {
158 fPretty.append("\t");
159 }
160 }
161 }
162
163 // newline is really a request to add a newline, if we are on a fresh line there is no reason
164 // to add another newline
165 void newline() {
166 if (!fFreshline) {
167 fFreshline = true;
168 fPretty.append("\n");
joshualittbab82ed2014-08-08 09:41:42 -0700169 }
170 }
171
Brian Osman6c431d52019-04-15 16:31:54 -0400172 bool fFreshline;
173 int fTabs;
joshualittbab82ed2014-08-08 09:41:42 -0700174 size_t fIndex, fLength;
joshualitt43466a12015-02-13 17:18:27 -0800175 const char* fInput;
Brian Osman93ba0a42017-08-14 14:48:10 -0400176 SkSL::String fPretty;
joshualitt43466a12015-02-13 17:18:27 -0800177
178 // Some helpers for parseUntil when we go over a string length
179 bool fInParseUntilNewline;
180 bool fInParseUntil;
181 const char* fInParseUntilToken;
joshualittbab82ed2014-08-08 09:41:42 -0700182};
183
Brian Osman6c431d52019-04-15 16:31:54 -0400184SkSL::String PrettyPrint(const SkSL::String& string) {
joshualittbab82ed2014-08-08 09:41:42 -0700185 GLSLPrettyPrint pp;
Brian Osman6c431d52019-04-15 16:31:54 -0400186 return pp.prettify(string);
joshualittbab82ed2014-08-08 09:41:42 -0700187}
188
Brian Salomone334c592017-05-15 11:00:58 -0400189} // namespace GrSKSLPrettyPrint