Skip to content

Commit edae090

Browse files
Merge pull request #1400 from QueryaHub/issue/1360-jsonc-single-pass-strings
perf(theme): single-pass string literals in stripJsonc
2 parents 333c5cf + d0fbc34 commit edae090

2 files changed

Lines changed: 44 additions & 29 deletions

File tree

‎lib/core/theme/parser/jsonc_preprocessor.dart‎

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ String stripJsonc(String input) {
99
final next = i + 1 < len ? input[i + 1] : '';
1010

1111
if (ch == '"') {
12-
out.write(_copyStringLiteral(input, i));
13-
i = _skipStringLiteral(input, i);
12+
// One scan finds the closing quote; the literal is copied as a slice.
13+
final end = _stringLiteralEnd(input, i);
14+
out.write(input.substring(i, end));
15+
i = end;
1416
continue;
1517
}
1618

@@ -54,37 +56,20 @@ String stripJsonc(String input) {
5456

5557
bool _isWhitespace(String c) => c == ' ' || c == '\t' || c == '\n' || c == '\r';
5658

57-
String _copyStringLiteral(String s, int start) {
58-
final buf = StringBuffer();
59-
var i = start;
60-
buf.write(s[i]);
61-
i++;
62-
while (i < s.length) {
63-
final ch = s[i];
64-
buf.write(ch);
65-
if (ch == '\\' && i + 1 < s.length) {
66-
i++;
67-
buf.write(s[i]);
68-
} else if (ch == '"') {
69-
i++;
70-
break;
71-
}
72-
i++;
73-
}
74-
return buf.toString();
75-
}
76-
77-
int _skipStringLiteral(String s, int start) {
59+
/// Offset after the string literal that opens at [start] (a `"`): past its
60+
/// closing quote, or the end of [s] when it is not terminated. A backslash
61+
/// escapes the next character.
62+
int _stringLiteralEnd(String s, int start) {
63+
final len = s.length;
7864
var i = start + 1;
79-
while (i < s.length) {
80-
if (s[i] == '\\') {
65+
while (i < len) {
66+
final c = s.codeUnitAt(i);
67+
if (c == 0x5C) {
8168
i += 2;
8269
continue;
8370
}
84-
if (s[i] == '"') {
85-
return i + 1;
86-
}
71+
if (c == 0x22) return i + 1;
8772
i++;
8873
}
89-
return s.length;
74+
return len;
9075
}

‎test/core/theme/parser/jsonc_preprocessor_test.dart‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,35 @@ void main() {
4242
expect(cleaned.contains(',}'), isFalse);
4343
expect(cleaned, contains('"editor.background"'));
4444
});
45+
46+
test('keeps string literals byte for byte, escapes included', () {
47+
const input = r'{"a": "q\"uote // not a comment", "b": "back\\"}';
48+
expect(stripJsonc(input), input);
49+
});
50+
51+
test('comment markers and trailing commas inside strings survive', () {
52+
const input = r'{"a": "/* x */", "b": "[1,]", "c": "x,}"}';
53+
expect(stripJsonc(input), input);
54+
});
55+
56+
test('an unterminated string is copied to the end', () {
57+
expect(stripJsonc('{"a": "oops'), '{"a": "oops');
58+
});
59+
60+
test('a trailing backslash at the end of input does not throw', () {
61+
expect(stripJsonc(r'{"a": "x\'), r'{"a": "x\');
62+
});
63+
64+
test('comments and trailing commas around many strings', () {
65+
final input = StringBuffer('{\n');
66+
for (var i = 0; i < 200; i++) {
67+
input.write(' // token $i\n "k$i": "#${i.toRadixString(16).padLeft(6, '0')}",\n');
68+
}
69+
input.write('}');
70+
final cleaned = stripJsonc(input.toString());
71+
expect(cleaned.contains('//'), isFalse);
72+
expect(cleaned.contains(',\n}'), isFalse);
73+
expect(cleaned, contains('"k199": "#0000c7"'));
74+
});
4575
});
4676
}

0 commit comments

Comments
 (0)