[DAS] Fixes quotes conversion to work the same everywhere
Fixes: https://github.com/dart-lang/sdk/issues/60218
Change-Id: Id249ff553cd500c827894a5a94a280f49f86a132
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/412321
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
Auto-Submit: Felipe Morschel <git@fmorschel.dev>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_quotes.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_quotes.dart
index 12d5c29..99ae233 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_quotes.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_quotes.dart
@@ -6,7 +6,6 @@
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
@@ -14,7 +13,7 @@
class ConvertQuotes extends _ConvertQuotes {
@override
- late bool _fromDouble;
+ late bool _fromSingle;
ConvertQuotes({required super.context});
@@ -32,17 +31,13 @@
Future<void> compute(ChangeBuilder builder) async {
var node = this.node;
if (node is SimpleStringLiteral) {
- _fromDouble = !node.isSingleQuoted;
- await _simpleStringLiteral(builder, node, addBackslash: false);
- await _removeBackslash(builder, node.literal);
+ _fromSingle = node.isSingleQuoted;
} else if (node is StringInterpolation) {
- _fromDouble = !node.isSingleQuoted;
- await _stringInterpolation(builder, node);
-
- for (var child in node.childEntities.whereType<InterpolationString>()) {
- await _removeBackslash(builder, child.contents);
- }
+ _fromSingle = node.isSingleQuoted;
+ } else if (node case InterpolationString(:StringInterpolation parent)) {
+ _fromSingle = parent.isSingleQuoted;
}
+ await super.compute(builder);
}
}
@@ -63,7 +58,7 @@
FixKind get multiFixKind => DartFixKind.CONVERT_TO_DOUBLE_QUOTED_STRING_MULTI;
@override
- bool get _fromDouble => false;
+ bool get _fromSingle => true;
}
class ConvertToSingleQuotes extends _ConvertQuotes {
@@ -83,116 +78,282 @@
FixKind get multiFixKind => DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING_MULTI;
@override
- bool get _fromDouble => true;
+ bool get _fromSingle => false;
}
abstract class _ConvertQuotes extends ResolvedCorrectionProducer {
+ static const _backslash = 0x5C;
+ static const _dollar = 0x24;
+
_ConvertQuotes({required super.context});
- /// Return `true` if this producer is converting from double quotes to single
- /// quotes, or `false` if it's converting from single quotes to double quotes.
- bool get _fromDouble;
+ /// Return `true` if this producer is converting from single quotes to double
+ /// quotes, or `false` if it's converting from double quotes to single quotes.
+ bool get _fromSingle;
+
+ _QuotePair get _quotes =>
+ _fromSingle ? _QuotePair.toDouble : _QuotePair.toSingle;
@override
Future<void> compute(ChangeBuilder builder) async {
var node = this.node;
+ StringInterpolation? interpolation;
if (node is SimpleStringLiteral) {
await _simpleStringLiteral(builder, node);
} else if (node is StringInterpolation) {
- await _stringInterpolation(builder, node);
- } else if (node is InterpolationString) {
- await _stringInterpolation(builder, node.parent as StringInterpolation);
+ interpolation = node;
+ } else if (node case InterpolationString(:StringInterpolation parent)) {
+ interpolation = parent;
}
- await _removeBackslash(builder, token);
+ await _stringInterpolation(builder, interpolation);
}
- Future<void> _addBackslash(ChangeBuilder builder, Token token) async {
- var quote = _fromDouble ? "'" : '"';
- var text = utils.getText(token.offset, token.length);
- for (var i = 1; i + 1 < text.length; i++) {
- if ((text[i + 1] == quote) && (text[i] != r'\')) {
- await builder.addDartFileEdit(file, (builder) {
- builder.addSimpleInsertion(token.offset + 1 + i, r'\');
- });
+ bool _canKeepAsRaw(String text) {
+ var newQuoteChar = _quotes.newQuoteString;
+
+ // If the string ends with the new quote, we would have four consecutive
+ // equal quotes, which would close the string and open a new one.
+ if (text.endsWith(newQuoteChar)) {
+ return false;
+ }
+
+ // If the string contains three consecutive new quotes (or more) we would
+ // close the string at that position so this would be invalid.
+ return !text.contains(newQuoteChar * 3);
+ }
+
+ Future<void> _deleteAtOffset(ChangeBuilder builder, int offset) async {
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addDeletion(SourceRange(offset, 1));
+ });
+ }
+
+ Future<void> _escapeBackslashesAndDollars(
+ ChangeBuilder builder,
+ String text,
+ int offset,
+ ) async {
+ for (var i = 0; i < text.length; i++) {
+ var char = text.codeUnitAt(i);
+ if (char == _backslash) {
+ await _insertBackslashAt(builder, offset + i);
+ } else if (char == _dollar) {
+ await _insertBackslashAt(builder, offset + i);
}
}
}
- Future<void> _removeBackslash(ChangeBuilder builder, Token token) async {
- var quote = _fromDouble ? '"' : "'";
- var text = utils.getText(token.offset, token.length);
- for (var i = 0; i + 1 < text.length; i++) {
- if (text[i] == r'\' && text[i + 1] == quote) {
- await builder.addDartFileEdit(file, (builder) {
- builder.addDeletion(SourceRange(token.offset + i, 1));
- });
- i++;
+ Future<void> _fixBackslashesForQuotes(
+ ChangeBuilder builder,
+ String text,
+ int offset, {
+ required bool isMultiline,
+ required bool containsStringEnd,
+ }) async {
+ var _QuotePair(:newQuote, :oppositeQuote) = _quotes;
+ var isEscaping = false;
+ var quoteCount = 0;
+
+ for (var i = 0; i < text.length; i++) {
+ var char = text.codeUnitAt(i);
+ if (char == newQuote) {
+ // If we're not escaping, add a backslash before the quote.
+ if (!isEscaping) {
+ if (isMultiline) {
+ quoteCount++;
+ // If we have a triple quote equal to the new quote, we need to
+ // escape the third so it doesn't close the string.
+ if (quoteCount == 3) {
+ await _insertBackslashAt(builder, offset + i);
+ // This quote counts as the first of a new triple quote.
+ quoteCount = 0;
+ } else if (containsStringEnd && (i + 1) == text.length) {
+ // At the end of the multiline string we must always escape the
+ // quote so that our multiline closing works as expected.
+ await _insertBackslashAt(builder, offset + i);
+ }
+ } else {
+ await _insertBackslashAt(builder, offset + i);
+ }
+ } else {
+ // Here we say that we're not escaping anymore, because this is a
+ // quote, not a backslash.
+ isEscaping = false;
+ }
+ } else {
+ quoteCount = 0;
+ if (char == oppositeQuote) {
+ // If we're escaping, remove the backslash before the opposite quote.
+ if (isEscaping) {
+ await _deleteAtOffset(builder, offset + i - 1);
+ // Here we say that we're not escaping anymore, because this is a
+ // quote, not a backslash.
+ isEscaping = false;
+ }
+ }
+ // Swap the escaping state because we found a backslash.
+ isEscaping = (char == _backslash) && !isEscaping;
}
}
}
+ Future<void> _insertBackslashAt(ChangeBuilder builder, int offset) async {
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addSimpleInsertion(offset, r'\');
+ });
+ }
+
+ String _newQuote(SingleStringLiteral node) {
+ return node.isMultiline
+ ? _quotes.newQuoteMultilineString
+ : _quotes.newQuoteString;
+ }
+
+ Future<void> _replaceQuotes(
+ ChangeBuilder builder,
+ int offset,
+ int end,
+ String newQuote, {
+ required bool wasRaw,
+ required bool isRaw,
+ }) async {
+ var endQuoteLength = newQuote.length;
+ var startQuoteLength = endQuoteLength;
+ var startQuoteOffset = offset;
+ if (wasRaw) {
+ if (isRaw) {
+ startQuoteOffset += 1;
+ } else {
+ startQuoteLength += 1;
+ }
+ }
+ await builder.addDartFileEdit(file, (builder) {
+ builder.addSimpleReplacement(
+ SourceRange(startQuoteOffset, startQuoteLength),
+ newQuote,
+ );
+ builder.addSimpleReplacement(
+ SourceRange(end - endQuoteLength, endQuoteLength),
+ newQuote,
+ );
+ });
+ }
+
Future<void> _simpleStringLiteral(
ChangeBuilder builder,
- SimpleStringLiteral node, {
- bool addBackslash = true,
- }) async {
- if (_fromDouble ? !node.isSingleQuoted : node.isSingleQuoted) {
- var newQuote =
- node.isMultiline
- ? (_fromDouble ? "'''" : '"""')
- : (_fromDouble ? "'" : '"');
- var quoteLength = node.isMultiline ? 3 : 1;
- var token = node.literal;
+ SimpleStringLiteral node,
+ ) async {
+ if (_fromSingle != node.isSingleQuoted) {
+ return;
+ }
+ var token = node.literal;
+ if (token.isSynthetic) {
+ return;
+ }
- if (addBackslash) {
- await _addBackslash(builder, token);
- }
-
- if (!token.isSynthetic) {
- await builder.addDartFileEdit(file, (builder) {
- builder.addSimpleReplacement(
- SourceRange(node.offset + (node.isRaw ? 1 : 0), quoteLength),
- newQuote,
- );
- builder.addSimpleReplacement(
- SourceRange(node.end - quoteLength, quoteLength),
- newQuote,
- );
- });
+ var isRaw = node.isRaw;
+ var endQuoteLength = node.isMultiline ? 3 : 1;
+ var startQuoteLength = endQuoteLength;
+ var offset = token.offset + startQuoteLength;
+ var text = utils.getText(
+ offset,
+ token.length - (startQuoteLength + endQuoteLength),
+ );
+ if (isRaw) {
+ offset++;
+ text = text.substring(1);
+ isRaw = _canKeepAsRaw(text);
+ // Removes obsolete escape for opposite quotes.
+ if (!isRaw) {
+ await _escapeBackslashesAndDollars(builder, text, offset);
}
}
+
+ await _fixBackslashesForQuotes(
+ builder,
+ text,
+ offset,
+ isMultiline: node.isMultiline,
+ containsStringEnd: true,
+ );
+
+ await _replaceQuotes(
+ builder,
+ node.offset,
+ node.end,
+ _newQuote(node),
+ isRaw: isRaw,
+ wasRaw: node.isRaw,
+ );
}
Future<void> _stringInterpolation(
ChangeBuilder builder,
- StringInterpolation node,
+ StringInterpolation? node,
) async {
- if (_fromDouble ? !node.isSingleQuoted : node.isSingleQuoted) {
- var newQuote =
- node.isMultiline
- ? (_fromDouble ? "'''" : '"""')
- : (_fromDouble ? "'" : '"');
- var quoteLength = node.isMultiline ? 3 : 1;
- var elements = node.elements;
- for (var i = 0; i < elements.length; i++) {
- var element = elements[i];
- if (element is InterpolationString) {
- var token = element.contents;
- if (token.isSynthetic || token.lexeme.contains(newQuote)) {
- return;
- }
- }
- }
- await builder.addDartFileEdit(file, (builder) {
- builder.addSimpleReplacement(
- SourceRange(node.offset + (node.isRaw ? 1 : 0), quoteLength),
- newQuote,
- );
- builder.addSimpleReplacement(
- SourceRange(node.end - quoteLength, quoteLength),
- newQuote,
- );
- });
+ if (node == null) {
+ return;
}
+ if (_fromSingle != node.isSingleQuoted) {
+ return;
+ }
+
+ if (node.lastString.endToken.isSynthetic ||
+ node.firstString.beginToken.isSynthetic) {
+ return;
+ }
+
+ var newQuote = _newQuote(node);
+
+ for (var element in node.elements) {
+ if (element is InterpolationString) {
+ var offset = element.offset;
+ var length = element.length;
+ var containsStringEnd = false;
+ if (element == node.firstString) {
+ offset += newQuote.length;
+ } else if (element == node.lastString) {
+ length -= newQuote.length;
+ containsStringEnd = true;
+ }
+ var text = utils.getText(offset, length);
+ await _fixBackslashesForQuotes(
+ builder,
+ text,
+ offset,
+ isMultiline: node.isMultiline,
+ containsStringEnd: containsStringEnd,
+ );
+ }
+ }
+
+ await _replaceQuotes(
+ builder,
+ node.offset,
+ node.end,
+ newQuote,
+ wasRaw: false,
+ isRaw: false,
+ );
}
}
+
+enum _QuotePair {
+ toDouble(_doubleQuote, '"', '"""', _singleQuote),
+ toSingle(_singleQuote, "'", "'''", _doubleQuote);
+
+ static const _doubleQuote = 0x22;
+ static const _singleQuote = 0x27;
+
+ final int newQuote;
+ final String newQuoteString;
+ final String newQuoteMultilineString;
+ final int oppositeQuote;
+
+ const _QuotePair(
+ this.newQuote,
+ this.newQuoteString,
+ this.newQuoteMultilineString,
+ this.oppositeQuote,
+ );
+}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_double_quoted_string_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_double_quoted_string_test.dart
index 80902e1..128c078 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_double_quoted_string_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_double_quoted_string_test.dart
@@ -19,13 +19,130 @@
@override
AssistKind get kind => DartAssistKind.CONVERT_TO_DOUBLE_QUOTED_STRING;
+ Future<void> test_interpolation_surroundedByEscapedQuote() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print('a \'$b\'');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print("a '$b'");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print('a \"$b\"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print("a \"$b\"");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2_left() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print('a \"$b"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print("a \"$b\"");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2_right() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print('a "$b\"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print("a \"$b\"");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote3() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(' \\"$b\\"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print(" \\\"$b\\\"");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote4() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(' \\\'$b\\\'');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print(" \\'$b\\'");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote5() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(' \\\\"$b\\\\"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print(" \\\\\"$b\\\\\"");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote6() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(' \\\\\'$b\\\\\'');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print(" \\\\'$b\\\\'");
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByQuotes() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print('a "$b"');
+}
+''');
+ await assertHasAssistAt("'", r'''
+void f(int b) {
+ print("a \"$b\"");
+}
+''');
+ }
+
Future<void> test_one_backslash() async {
await resolveTestCode(r'''
void f() {
print('a\'b\'c');
}
''');
- await assertHasAssistAt("'a", r'''
+ await assertHasAssistAt("'", r'''
void f() {
print("a'b'c");
}
@@ -38,7 +155,7 @@
print('a"b"c');
}
''');
- await assertHasAssistAt("'a", r'''
+ await assertHasAssistAt("'", r'''
void f() {
print("a\"b\"c");
}
@@ -51,21 +168,21 @@
print("abc");
}
''');
- await assertNoAssistAt('"ab');
+ await assertNoAssistAt('"');
}
Future<void> test_one_interpolation() async {
await resolveTestCode(r'''
void f() {
- var b = 'b';
- var c = 'c';
+ var b = "b";
+ var c = "c";
print('a $b-${c} d');
}
''');
- await assertHasAssistAt(r"'a $b", r'''
+ await assertHasAssistAt(r"'", r'''
void f() {
- var b = 'b';
- var c = 'c';
+ var b = "b";
+ var c = "c";
print("a $b-${c} d");
}
''');
@@ -87,7 +204,7 @@
print(r'abc');
}
''');
- await assertHasAssistAt("'ab", '''
+ await assertHasAssistAt("'", '''
void f() {
print(r"abc");
}
@@ -100,7 +217,7 @@
print('abc');
}
''');
- await assertHasAssistAt("'ab", '''
+ await assertHasAssistAt("'", '''
void f() {
print("abc");
}
@@ -117,15 +234,94 @@
await assertNoAssistAt("'");
}
+ Future<void> test_raw_multiLine_manyQuotes() async {
+ await resolveTestCode("""
+void f() {
+ print(r'''
+""\"""\"""\"""
+''');
+}
+""");
+ await assertHasAssistAt("r'", r'''
+void f() {
+ print("""
+""\"""\"""\"""
+""");
+}
+''');
+ }
+
+ Future<void> test_raw_multiLine_threeQuotes() async {
+ await resolveTestCode("""
+void f() {
+ print(r'''
+""\"''');
+}
+""");
+ await assertHasAssistAt("r'", r'''
+void f() {
+ print("""
+""\"""");
+}
+''');
+ }
+
+ Future<void> test_raw_multiLine_twoQuotes() async {
+ await resolveTestCode(r"""
+void f() {
+ print(r'''
+''\''\'
+""
+''');
+}
+""");
+ await assertHasAssistAt("r'", '''
+void f() {
+ print(r"""
+''\''\'
+""
+""");
+}
+''');
+ }
+
+ Future<void> test_raw_multiLine_twoQuotesAtEnd() async {
+ await resolveTestCode("""
+void f() {
+ print(r'''
+""''');
+}
+""");
+ await assertHasAssistAt("r'", r'''
+void f() {
+ print("""
+"\"""");
+}
+''');
+ }
+
+ Future<void> test_raw_nonEscapedChars() async {
+ await resolveTestCode(r'''
+void f() {
+ print(r'\$"');
+}
+''');
+ await assertHasAssistAt("r'", r'''
+void f() {
+ print("\\\$\"");
+}
+''');
+ }
+
Future<void> test_three_embeddedTarget() async {
await resolveTestCode("""
void f() {
print('''a""\"c''');
}
""");
- await assertHasAssistAt("'a", r'''
+ await assertHasAssistAt("'", r'''
void f() {
- print("""a\"\"\"c""");
+ print("""a""\"c""");
}
''');
}
@@ -136,21 +332,21 @@
print("""abc""");
}
''');
- await assertNoAssistAt('"ab');
+ await assertNoAssistAt('"');
}
Future<void> test_three_interpolation() async {
await resolveTestCode(r"""
void f() {
- var b = 'b';
- var c = 'c';
+ var b = "b";
+ var c = "c";
print('''a $b-${c} d''');
}
""");
- await assertHasAssistAt(r"'a $b", r'''
+ await assertHasAssistAt(r"'", r'''
void f() {
- var b = 'b';
- var c = 'c';
+ var b = "b";
+ var c = "c";
print("""a $b-${c} d""");
}
''');
@@ -162,7 +358,7 @@
print(r'''abc''');
}
""");
- await assertHasAssistAt("'ab", '''
+ await assertHasAssistAt("'", '''
void f() {
print(r"""abc""");
}
@@ -175,7 +371,7 @@
print('''abc''');
}
""");
- await assertHasAssistAt("'ab", '''
+ await assertHasAssistAt("'", '''
void f() {
print("""abc""");
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_single_quoted_string_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_single_quoted_string_test.dart
index 9572d7a..9fc7738 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_single_quoted_string_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_single_quoted_string_test.dart
@@ -20,13 +20,130 @@
@override
AssistKind get kind => DartAssistKind.CONVERT_TO_SINGLE_QUOTED_STRING;
+ Future<void> test_interpolation_surroundedByEscapedQuote() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print("a \'$b\'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print('a \'$b\'');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print("a \"$b\"");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print('a "$b"');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2_left() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print("a \'$b'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print('a \'$b\'');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote2_right() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print("a '$b\'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print('a \'$b\'');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote3() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(" \\'$b\\'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print(' \\\'$b\\\'');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote4() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(" \\\"$b\\\"");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print(' \\"$b\\"');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote5() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(" \\\\'$b\\\\'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print(' \\\\\'$b\\\\\'');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByEscapedQuote6() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print(" \\\\\"$b\\\\\"");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print(' \\\\"$b\\\\"');
+}
+''');
+ }
+
+ Future<void> test_interpolation_surroundedByQuotes() async {
+ await resolveTestCode(r'''
+void f(int b) {
+ print("a '$b'");
+}
+''');
+ await assertHasAssistAt('"', r'''
+void f(int b) {
+ print('a \'$b\'');
+}
+''');
+ }
+
Future<void> test_one_backslash() async {
await resolveTestCode(r'''
void f() {
print("a\"b\"c");
}
''');
- await assertHasAssistAt('"a', r"""
+ await assertHasAssistAt('"', r"""
void f() {
print('a"b"c');
}
@@ -39,7 +156,7 @@
print("a'b'c");
}
''');
- await assertHasAssistAt('"a', r'''
+ await assertHasAssistAt('"', r'''
void f() {
print('a\'b\'c');
}
@@ -52,7 +169,7 @@
print('abc');
}
''');
- await assertNoAssistAt("'ab");
+ await assertNoAssistAt("'");
}
Future<void> test_one_interpolation() async {
@@ -63,7 +180,7 @@
print("a $b-${c} d");
}
''');
- await assertHasAssistAt(r'"a $b', r'''
+ await assertHasAssistAt(r'"', r'''
void f() {
var b = 'b';
var c = 'c';
@@ -88,7 +205,7 @@
print(r"abc");
}
''');
- await assertHasAssistAt('"ab', '''
+ await assertHasAssistAt('"', '''
void f() {
print(r'abc');
}
@@ -101,7 +218,7 @@
print("abc");
}
''');
- await assertHasAssistAt('"ab', '''
+ await assertHasAssistAt('"', '''
void f() {
print('abc');
}
@@ -129,15 +246,94 @@
await assertNoAssistAt('"');
}
+ Future<void> test_raw_multiLine_manyQuotes() async {
+ await resolveTestCode('''
+void f() {
+ print(r"""
+''\'''\'''\'''
+""");
+}
+''');
+ await assertHasAssistAt('r"', r"""
+void f() {
+ print('''
+''\'''\'''\'''
+''');
+}
+""");
+ }
+
+ Future<void> test_raw_multiLine_threeQuotes() async {
+ await resolveTestCode('''
+void f() {
+ print(r"""
+''\'""");
+}
+''');
+ await assertHasAssistAt('r"', r"""
+void f() {
+ print('''
+''\'''');
+}
+""");
+ }
+
+ Future<void> test_raw_multiLine_twoQuotes() async {
+ await resolveTestCode(r'''
+void f() {
+ print(r"""
+""\""\"
+''
+""");
+}
+''');
+ await assertHasAssistAt('r"', """
+void f() {
+ print(r'''
+""\""\"
+''
+''');
+}
+""");
+ }
+
+ Future<void> test_raw_multiLine_twoQuotesAtEnd() async {
+ await resolveTestCode('''
+void f() {
+ print(r"""
+''""");
+}
+''');
+ await assertHasAssistAt('r"', r"""
+void f() {
+ print('''
+'\'''');
+}
+""");
+ }
+
+ Future<void> test_raw_nonEscapedChars() async {
+ await resolveTestCode(r"""
+void f() {
+ print(r"\$'");
+}
+""");
+ await assertHasAssistAt('r"', r"""
+void f() {
+ print('\\\$\'');
+}
+""");
+ }
+
Future<void> test_three_embeddedTarget() async {
await resolveTestCode('''
void f() {
print("""a''\'bc""");
}
''');
- await assertHasAssistAt('"a', r"""
+ await assertHasAssistAt('"', r"""
void f() {
- print('''a\'\'\'bc''');
+ print('''a''\'bc''');
}
""");
}
@@ -148,7 +344,7 @@
print('''abc''');
}
""");
- await assertNoAssistAt("'ab");
+ await assertNoAssistAt("'");
}
Future<void> test_three_interpolation() async {
@@ -159,7 +355,7 @@
print("""a $b-${c} d""");
}
''');
- await assertHasAssistAt(r'"a $b', r"""
+ await assertHasAssistAt(r'"', r"""
void f() {
var b = 'b';
var c = 'c';
@@ -174,7 +370,7 @@
print(r"""abc""");
}
''');
- await assertHasAssistAt('"ab', """
+ await assertHasAssistAt('"', """
void f() {
print(r'''abc''');
}
@@ -187,7 +383,7 @@
print("""abc""");
}
''');
- await assertHasAssistAt('"ab', """
+ await assertHasAssistAt('"', """
void f() {
print('''abc''');
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_double_quoted_string_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_double_quoted_string_test.dart
index b9d6a59..2803077 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_double_quoted_string_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_double_quoted_string_test.dart
@@ -85,7 +85,8 @@
''');
}
- /// More coverage in the `convert_to_double_quoted_string_test.dart` assist test.
+ /// More coverage in the `convert_to_double_quoted_string_test.dart` assist
+ /// test.
Future<void> test_one_simple() async {
await resolveTestCode('''
void f() {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/convert_to_single_quoted_string_test.dart b/pkg/analysis_server/test/src/services/correction/fix/convert_to_single_quoted_string_test.dart
index 48d6b0c..956261f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/convert_to_single_quoted_string_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/convert_to_single_quoted_string_test.dart
@@ -85,7 +85,8 @@
''');
}
- /// More coverage in the `convert_to_single_quoted_string_test.dart` assist test.
+ /// More coverage in the `convert_to_single_quoted_string_test.dart` assist
+ /// test.
Future<void> test_one_simple() async {
await resolveTestCode('''
void f() {