Skip to content

Commit 6df8cd9

Browse files
glebmxzyfer
authored andcommitted
Newlines: Parse \f and normalize in comments (#2849)
Also: 1. Moves the `normalize*` functions and `rtrim` to a separate object file, so that they can be unit tested quickly and easily. 2. Changes `string_to_output` to also replace `\r\n`, fixing Windows tests. 3. Improves other `normalize_*` functions and adds tests. Refs #2843 sass-spec PRs: 1. Bug fixes to make it run: sass/sass-spec#1365 2. Enabling the newly passing tests: sass/sass-spec#1366
1 parent ceedaeb commit 6df8cd9

17 files changed

+276
-73
lines changed

Makefile.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ SOURCES = \
2929
bind.cpp \
3030
file.cpp \
3131
util.cpp \
32+
util_string.cpp \
3233
json.cpp \
3334
units.cpp \
3435
values.cpp \

src/emitter.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "context.hpp"
44
#include "output.hpp"
55
#include "emitter.hpp"
6+
#include "util_string.hpp"
67
#include "utf8_string.hpp"
78

89
namespace Sass {
@@ -134,13 +135,13 @@ namespace Sass {
134135
// write space/lf
135136
flush_schedules();
136137

137-
if (in_comment && output_style() == COMPACT) {
138-
// unescape comment nodes
139-
std::string out = comment_to_string(text);
140-
// add to buffer
141-
wbuf.buffer += out;
142-
// account for data in source-maps
138+
if (in_comment) {
139+
std::string out = Util::normalize_newlines(text);
140+
if (output_style() == COMPACT) {
141+
out = comment_to_compact_string(out);
142+
}
143143
wbuf.smap.append(Offset(out));
144+
wbuf.buffer += std::move(out);
144145
} else {
145146
// add to buffer
146147
wbuf.buffer += text;

src/eval.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "expand.hpp"
3131
#include "color_maps.hpp"
3232
#include "sass_functions.hpp"
33+
#include "util_string.hpp"
3334

3435
namespace Sass {
3536

src/fn_miscs.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "expand.hpp"
33
#include "fn_utils.hpp"
44
#include "fn_miscs.hpp"
5+
#include "util_string.hpp"
56

67
namespace Sass {
78

src/fn_utils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "parser.hpp"
66
#include "fn_utils.hpp"
7+
#include "util_string.hpp"
78

89
namespace Sass {
910

src/lexer.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,11 @@ namespace Sass {
153153
// Match word boundary (zero-width lookahead).
154154
const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; }
155155

156-
// Match linefeed /(?:\n|\r\n?)/
156+
// Match linefeed /(?:\n|\r\n?|\f)/
157157
const char* re_linebreak(const char* src)
158158
{
159159
// end of file or unix linefeed return here
160-
if (*src == 0 || *src == '\n') return src + 1;
160+
if (*src == 0 || *src == '\n' || *src == '\f') return src + 1;
161161
// a carriage return may optionally be followed by a linefeed
162162
if (*src == '\r') return *(src + 1) == '\n' ? src + 2 : src + 1;
163163
// no linefeed
@@ -169,7 +169,7 @@ namespace Sass {
169169
const char* end_of_line(const char* src)
170170
{
171171
// end of file or unix linefeed return here
172-
return *src == 0 || *src == '\n' || *src == '\r' ? src : 0;
172+
return *src == 0 || *src == '\n' || *src == '\r' || *src == '\f' ? src : 0;
173173
}
174174

175175
// Assert end_of_file boundary (/\z/)

src/output.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ namespace Sass {
9292

9393
void Output::operator()(Comment* c)
9494
{
95-
std::string txt = c->text()->to_string(opt);
9695
// if (indentation && txt == "/**/") return;
9796
bool important = c->is_important();
9897
if (output_style() != COMPRESSED || important) {

src/parser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "color_maps.hpp"
1212
#include "sass/functions.h"
1313
#include "error_handling.hpp"
14+
#include "util_string.hpp"
1415

1516
// Notes about delayed: some ast nodes can have delayed evaluation so
1617
// they can preserve their original semantics if needed. This is most

src/prelexer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ namespace Sass {
263263
>(src);
264264
}
265265

266-
// Match a line comment (/.*?(?=\n|\r\n?|\Z)/.
266+
// Match a line comment (/.*?(?=\n|\r\n?|\f|\Z)/.
267267
const char* line_comment(const char* src)
268268
{
269269
return sequence<

src/util.cpp

Lines changed: 45 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -163,42 +163,64 @@ namespace Sass {
163163
std::replace(str.begin(), str.end(), '\n', ' ');
164164
}
165165

166-
// bell characters are replaced with spaces
167-
// also eats spaces after line-feeds (ltrim)
166+
// 1. Removes whitespace after newlines.
167+
// 2. Replaces newlines with spaces.
168+
//
169+
// This method only considers LF and CRLF as newlines.
168170
std::string string_to_output(const std::string& str)
169171
{
170-
std::string out("");
171-
bool lf = false;
172-
for (auto i : str) {
173-
if (i == '\n') {
174-
out += ' ';
175-
lf = true;
176-
} else if (!(lf && isspace(i))) {
177-
out += i;
178-
lf = false;
172+
std::string result;
173+
result.reserve(str.size());
174+
std::size_t pos = 0;
175+
while (true) {
176+
const std::size_t newline = str.find_first_of("\n\r", pos);
177+
if (newline == std::string::npos) break;
178+
result.append(str, pos, newline - pos);
179+
if (str[newline] == '\r') {
180+
if (str[newline + 1] == '\n') {
181+
pos = newline + 2;
182+
} else {
183+
// CR without LF: append as-is and continue.
184+
result += '\r';
185+
pos = newline + 1;
186+
continue;
187+
}
188+
} else {
189+
pos = newline + 1;
190+
}
191+
result += ' ';
192+
const std::size_t non_space = str.find_first_not_of(" \f\n\r\t\v", pos);
193+
if (non_space != std::string::npos) {
194+
pos = non_space;
179195
}
180196
}
181-
return out;
197+
result.append(str, pos, std::string::npos);
198+
return result;
182199
}
183200

184201
std::string escape_string(const std::string& str)
185202
{
186-
std::string out("");
187-
for (auto i : str) {
188-
if (i == '\n') {
189-
out += "\\n";
190-
} else if (i == '\r') {
191-
out += "\\r";
192-
} else if (i == '\t') {
193-
out += "\\t";
194-
} else {
195-
out += i;
203+
std::string out;
204+
out.reserve(str.size());
205+
for (char c : str) {
206+
switch (c) {
207+
case '\n':
208+
out.append("\\n");
209+
break;
210+
case '\r':
211+
out.append("\\r");
212+
break;
213+
case '\f':
214+
out.append("\\f");
215+
break;
216+
default:
217+
out += c;
196218
}
197219
}
198220
return out;
199221
}
200222

201-
std::string comment_to_string(const std::string& text)
223+
std::string comment_to_compact_string(const std::string& text)
202224
{
203225
std::string str = "";
204226
size_t has = 0;
@@ -207,7 +229,6 @@ namespace Sass {
207229
for (auto i : text) {
208230
if (clean) {
209231
if (i == '\n') { has = 0; }
210-
else if (i == '\r') { has = 0; }
211232
else if (i == '\t') { ++ has; }
212233
else if (i == ' ') { ++ has; }
213234
else if (i == '*') {}
@@ -219,8 +240,6 @@ namespace Sass {
219240
}
220241
} else if (i == '\n') {
221242
clean = true;
222-
} else if (i == '\r') {
223-
clean = true;
224243
} else {
225244
str += i;
226245
}
@@ -508,33 +527,6 @@ namespace Sass {
508527
}
509528

510529
namespace Util {
511-
using std::string;
512-
513-
std::string rtrim(const std::string &str) {
514-
std::string trimmed = str;
515-
size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");
516-
if (pos_ws != std::string::npos)
517-
{ trimmed.erase(pos_ws + 1); }
518-
else { trimmed.clear(); }
519-
return trimmed;
520-
}
521-
522-
std::string normalize_underscores(const std::string& str) {
523-
std::string normalized = str;
524-
for(size_t i = 0, L = normalized.length(); i < L; ++i) {
525-
if(normalized[i] == '_') {
526-
normalized[i] = '-';
527-
}
528-
}
529-
return normalized;
530-
}
531-
532-
std::string normalize_decimals(const std::string& str) {
533-
std::string prefix = "0";
534-
std::string normalized = str;
535-
536-
return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;
537-
}
538530

539531
bool isPrintable(Ruleset* r, Sass_Output_Style style) {
540532
if (r == NULL) {

0 commit comments

Comments
 (0)