Skip to content

Commit a838dcf

Browse files
authored
Don't complete quotes if they complete a valid string (#60227)
1 parent 38885db commit a838dcf

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLiteralCompletionTests.cs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,71 @@ void Method()
429429
CheckStart(session.Session, expectValidSession: false);
430430
}
431431

432+
[WpfFact, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)]
433+
[WorkItem(59178, "https://github.com/dotnet/roslyn/issues/59178")]
434+
public void String_CompleteLiteral()
435+
{
436+
var code = @"class C
437+
{
438+
void Method()
439+
{
440+
var s = ""this"" + $$that"";
441+
}
442+
}";
443+
using var session = CreateSessionDoubleQuote(code);
444+
Assert.NotNull(session);
445+
CheckStart(session.Session, expectValidSession: false);
446+
}
447+
448+
[WpfFact, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)]
449+
[WorkItem(59178, "https://github.com/dotnet/roslyn/issues/59178")]
450+
public void String_BeforeOtherString1()
451+
{
452+
var code = @"class C
453+
{
454+
void Method()
455+
{
456+
var s = $$ + "" + bar"";
457+
}
458+
}";
459+
using var session = CreateSessionDoubleQuote(code);
460+
Assert.NotNull(session);
461+
CheckStart(session.Session);
462+
}
463+
464+
[WpfFact, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)]
465+
[WorkItem(59178, "https://github.com/dotnet/roslyn/issues/59178")]
466+
public void String_BeforeOtherString2()
467+
{
468+
var code = @"class C
469+
{
470+
void Method()
471+
{
472+
var s = $$ + ""; } "";
473+
}
474+
}";
475+
using var session = CreateSessionDoubleQuote(code);
476+
Assert.NotNull(session);
477+
CheckStart(session.Session);
478+
}
479+
480+
[WpfFact, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)]
481+
[WorkItem(59178, "https://github.com/dotnet/roslyn/issues/59178")]
482+
public void String_DontCompleteVerbatim()
483+
{
484+
var code = @"class C
485+
{
486+
void Method()
487+
{
488+
var s = ""this"" + @$$that
489+
and this"";
490+
}
491+
}";
492+
using var session = CreateSessionDoubleQuote(code);
493+
Assert.NotNull(session);
494+
CheckStart(session.Session);
495+
}
496+
432497
internal static Holder CreateSessionSingleQuote(string code)
433498
{
434499
return CreateSession(

src/Features/CSharp/Portable/BraceCompletion/StringLiteralBraceCompletionService.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading;
88
using System.Threading.Tasks;
99
using Microsoft.CodeAnalysis.BraceCompletion;
10+
using Microsoft.CodeAnalysis.CSharp.Extensions;
1011
using Microsoft.CodeAnalysis.Host.Mef;
1112
using Microsoft.CodeAnalysis.LanguageServices;
1213
using Microsoft.CodeAnalysis.Shared.Extensions;
@@ -53,6 +54,27 @@ protected override Task<bool> IsValidOpenBraceTokenAtPositionAsync(SyntaxToken t
5354
return SpecializedTasks.False;
5455
}
5556

57+
// If the single token that the user typed is a string literal that is more than just
58+
// the one double quote character they typed, and the line doesn't have errors, then
59+
// it means it is completing an existing token, from the start. For example given:
60+
//
61+
// var s = "te$$st";
62+
//
63+
// When the user types `" + "` to split the string into two literals, the first
64+
// quote won't be completed (because its in a string literal), and with this check
65+
// the second quote won't either.
66+
//
67+
// We don't do this optimization for verbatim strings because they are multi-line so
68+
// the flow on effects from us getting it wrong are much greater, and it can really change
69+
// the tree.
70+
if (token.IsKind(SyntaxKind.StringLiteralToken) &&
71+
!token.IsVerbatimStringLiteral() &&
72+
token.Span.Length > 1 &&
73+
!RestOfLineContainsDiagnostics(token))
74+
{
75+
return SpecializedTasks.False;
76+
}
77+
5678
if (token.SpanStart == position)
5779
{
5880
return SpecializedTasks.True;
@@ -63,5 +85,21 @@ protected override Task<bool> IsValidOpenBraceTokenAtPositionAsync(SyntaxToken t
6385
// is the @ character and the " is one past the token start.
6486
return Task.FromResult(token.SpanStart + 1 == position && token.IsVerbatimStringLiteral());
6587
}
88+
89+
private static bool RestOfLineContainsDiagnostics(SyntaxToken token)
90+
{
91+
while (!token.TrailingTrivia.Contains(t => t.IsEndOfLine()))
92+
{
93+
if (token.ContainsDiagnostics)
94+
return true;
95+
96+
token = token.GetNextToken();
97+
}
98+
99+
if (token.ContainsDiagnostics)
100+
return true;
101+
102+
return false;
103+
}
66104
}
67105
}

0 commit comments

Comments
 (0)