|
2 | 2 | ///@file |
3 | 3 |
|
4 | 4 | #include "eval.hh" |
| 5 | +#include <variant> |
5 | 6 |
|
6 | 7 | namespace nix { |
7 | 8 |
|
@@ -172,57 +173,58 @@ enum IndentChar { |
172 | 173 | Space = ' ', |
173 | 174 | }; |
174 | 175 |
|
| 176 | +struct StripState { |
| 177 | + std::optional<IndentChar> indentChar = std::nullopt; |
| 178 | + bool start = true; |
| 179 | + size_t min = -1; |
| 180 | + size_t cur = 0; |
| 181 | + bool indent(char c) { |
| 182 | + if (!start) return true; |
| 183 | + indentChar = indentChar.value_or(IndentChar(c)); |
| 184 | + bool ok = (indentChar == c); |
| 185 | + cur += ok; |
| 186 | + return ok; |
| 187 | + } |
| 188 | + void stop() { |
| 189 | + start = false; |
| 190 | + min = std::min(min, cur); |
| 191 | + } |
| 192 | + void reset() { |
| 193 | + start = true; |
| 194 | + cur = 0; |
| 195 | + } |
| 196 | +}; |
| 197 | + |
175 | 198 | inline Expr * ParserState::stripIndentation(const PosIdx pos, |
176 | 199 | std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es) |
177 | 200 | { |
178 | 201 | if (es.empty()) return new ExprString(""); |
179 | 202 |
|
180 | 203 | /* Figure out the minimum indentation. Note that by design |
181 | 204 | whitespace-only final lines are not taken into account. (So |
182 | | - the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */ |
| 205 | + the " " in "\n ''" is ignored, but the " " in "\n foo''" is not.) */ |
183 | 206 | std::optional<IndentChar> indentChar = std::nullopt; |
184 | | - bool atStartOfLine = true; /* = seen only whitespace in the current line */ |
185 | | - size_t minIndent = 1000000; |
186 | | - size_t curIndent = 0; |
| 207 | + StripState state; |
187 | 208 | for (auto & [i_pos, i] : es) { |
188 | | - auto * str = std::get_if<StringToken>(&i); |
189 | | - if (!str || !str->hasIndentation) { |
190 | | - /* Anti-quotations and escaped characters end the current start-of-line whitespace. */ |
191 | | - if (atStartOfLine) { |
192 | | - atStartOfLine = false; |
193 | | - if (curIndent < minIndent) minIndent = curIndent; |
194 | | - } |
| 209 | + StringToken* x = std::get_if<StringToken>(&i); |
| 210 | + if (!x || !x->hasIndentation) { |
| 211 | + state.stop(); |
195 | 212 | continue; |
196 | | - } |
197 | | - for (size_t j = 0; j < str->l; ++j) { |
198 | | - auto cur = str->p[j]; |
199 | | - if (atStartOfLine) { |
200 | | - if ( |
201 | | - indentChar == cur |
202 | | - || (!indentChar && (cur == ' ' || cur == '\t')) |
203 | | - ) { |
204 | | - if (!indentChar) { |
205 | | - indentChar = IndentChar(cur); |
206 | | - } |
207 | | - curIndent++; |
208 | | - } else if (cur == '\n') { |
209 | | - /* Empty line, doesn't influence minimum |
210 | | - indentation. */ |
211 | | - curIndent = 0; |
212 | | - } else { |
213 | | - atStartOfLine = false; |
214 | | - if (curIndent < minIndent) minIndent = curIndent; |
215 | | - } |
216 | | - } else if (cur == '\n') { |
217 | | - atStartOfLine = true; |
218 | | - curIndent = 0; |
| 213 | + }; |
| 214 | + std::string_view view = *x; |
| 215 | + for (auto cur: view) { |
| 216 | + if (cur == '\n') { |
| 217 | + state.reset(); |
| 218 | + continue; |
219 | 219 | } |
| 220 | + if (!state.indent(cur)) state.stop(); |
220 | 221 | } |
221 | 222 | } |
222 | 223 |
|
| 224 | + size_t minIndent = state.min; |
223 | 225 | /* Strip spaces from each line. */ |
224 | 226 | auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>; |
225 | | - atStartOfLine = true; |
| 227 | + bool atStartOfLine = true; |
226 | 228 | size_t curDropped = 0; |
227 | 229 | size_t n = es.size(); |
228 | 230 | auto i = es.begin(); |
|
0 commit comments