Skip to content

Commit 2a61b54

Browse files
Improve raw string completion (#77742)
Fixes #77724.
2 parents 38f239f + 53105cf commit 2a61b54

File tree

5 files changed

+45
-23
lines changed

5 files changed

+45
-23
lines changed

src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private static bool TryGetStartingNode(
172172

173173
startingNode = tokenOnLeft.GetRequiredParent();
174174

175-
// If the caret is before an opening delimiter or after a closing delimeter,
175+
// If the caret is before an opening delimiter or after a closing delimiter,
176176
// start analysis with node outside of delimiters.
177177
//
178178
// Examples,

src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_TypeChar.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHa
5353
var textChangeOpt =
5454
TryGenerateInitialEmptyRawString(caret.Value, cancellationToken) ??
5555
TryGrowInitialEmptyRawString(caret.Value, cancellationToken) ??
56-
TryGrowRawStringDelimeters(caret.Value, cancellationToken);
56+
TryGrowRawStringDelimiters(caret.Value, cancellationToken);
5757

5858
if (textChangeOpt is not TextChange textChange)
5959
return false;
@@ -115,17 +115,22 @@ private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHa
115115
if (token.SpanStart != start)
116116
return null;
117117

118-
if (token.Kind() is not (SyntaxKind.StringLiteralToken or SyntaxKind.InterpolatedStringStartToken or SyntaxKind.InterpolatedSingleLineRawStringStartToken))
118+
if (token.Kind() is not (SyntaxKind.StringLiteralToken or
119+
SyntaxKind.InterpolatedStringStartToken or
120+
SyntaxKind.InterpolatedSingleLineRawStringStartToken or
121+
SyntaxKind.InterpolatedMultiLineRawStringStartToken))
122+
{
119123
return null;
124+
}
120125

121126
return new TextChange(new TextSpan(position + 1, 0), "\"\"\"");
122127
}
123128

124129
/// <summary>
125130
/// When typing <c>"</c> given a raw string like <c>"""$$"""</c> (or a similar multiline form), then update the
126131
/// text to be: <c>""""$$""""</c>. i.e. grow both the start and end delimiters to keep the string properly
127-
/// balanced. This differs from TryGrowRawStringDelimeters in that the language will consider that initial
128-
/// <c>""""""</c> text to be a single delimeter, while we want to treat it as two.
132+
/// balanced. This differs from TryGrowRawStringDelimiters in that the language will consider that initial
133+
/// <c>""""""</c> text to be a single delimiter, while we want to treat it as two.
129134
/// </summary>
130135
private static TextChange? TryGrowInitialEmptyRawString(
131136
SnapshotPoint caret,
@@ -183,15 +188,15 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or
183188
/// update the text to be: <c>"""" goo bar """"</c>. i.e. grow both the start and end delimiters to keep the
184189
/// string properly balanced.
185190
/// </summary>
186-
private static TextChange? TryGrowRawStringDelimeters(
191+
private static TextChange? TryGrowRawStringDelimiters(
187192
SnapshotPoint caret,
188193
CancellationToken cancellationToken)
189194
{
190195
var snapshot = caret.Snapshot;
191196
var position = caret.Position;
192197

193198
// if we have """$$" then typing `"` here should not grow the start/end quotes. we only want to grow them
194-
// if the user is at the end of the start delimeter.
199+
// if the user is at the end of the start delimiter.
195200
if (position < snapshot.Length && snapshot[position] == '"')
196201
return null;
197202

src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,27 @@ public void TestGenerateWithInterpolatedString_TwoDollarSigns()
616616
"""", withSpansOnly: true);
617617
}
618618

619+
[WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/77724")]
620+
public void TestGenerateWithInterpolatedString_TwoDollarSigns_InLocalFunction()
621+
{
622+
using var testState = RawStringLiteralTestState.CreateTestState(
623+
"""
624+
void M()
625+
{
626+
var v = $$""[||]
627+
}
628+
""", withSpansOnly: true);
629+
630+
testState.SendTypeChar('"');
631+
testState.AssertCodeIs(
632+
""""
633+
void M()
634+
{
635+
var v = $$"""[||]"""
636+
}
637+
"""", withSpansOnly: true);
638+
}
639+
619640
[WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")]
620641
public void TestGenerateWithInterpolatedString_TwoDollarSigns_FourthDoubleQuote()
621642
{

src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
namespace Microsoft.CodeAnalysis.CSharp.NavigationBar;
2222

2323
[ExportLanguageService(typeof(INavigationBarItemService), LanguageNames.CSharp), Shared]
24-
internal class CSharpNavigationBarItemService : AbstractNavigationBarItemService
24+
[method: ImportingConstructor]
25+
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
26+
internal sealed class CSharpNavigationBarItemService() : AbstractNavigationBarItemService
2527
{
2628
private static readonly SymbolDisplayFormat s_typeFormat =
2729
SymbolDisplayFormat.CSharpErrorMessageFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeVariance);
@@ -39,12 +41,6 @@ internal class CSharpNavigationBarItemService : AbstractNavigationBarItemService
3941
SymbolDisplayMiscellaneousOptions.AllowDefaultLiteral |
4042
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
4143

42-
[ImportingConstructor]
43-
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
44-
public CSharpNavigationBarItemService()
45-
{
46-
}
47-
4844
protected override async Task<ImmutableArray<RoslynNavigationBarItem>> GetItemsInCurrentProcessAsync(
4945
Document document, bool supportsCodeGeneration, CancellationToken cancellationToken)
5046
{

src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -349,32 +349,32 @@ SyntaxKind.Utf8SingleLineRawStringLiteralToken or
349349
if (kind is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken)
350350
{
351351
var sourceText = token.SyntaxTree!.GetText(cancellationToken);
352-
var startDelimeterLength = 0;
353-
var endDelimeterLength = 0;
352+
var startDelimiterLength = 0;
353+
var endDelimiterLength = 0;
354354
for (int i = token.SpanStart, n = token.Span.End; i < n; i++)
355355
{
356356
if (sourceText[i] != '"')
357357
break;
358358

359-
startDelimeterLength++;
359+
startDelimiterLength++;
360360
}
361361

362362
for (int i = token.Span.End - 1, n = token.Span.Start; i >= n; i--)
363363
{
364364
if (sourceText[i] != '"')
365365
break;
366366

367-
endDelimeterLength++;
367+
endDelimiterLength++;
368368
}
369369

370-
return token.Span.Length == startDelimeterLength ||
371-
(token.Span.Length > startDelimeterLength && endDelimeterLength < startDelimeterLength);
370+
return token.Span.Length == startDelimiterLength ||
371+
(token.Span.Length > startDelimiterLength && endDelimiterLength < startDelimiterLength);
372372
}
373373
else
374374
{
375-
var startDelimeterLength = token.IsVerbatimStringLiteral() ? 2 : 1;
376-
return token.Span.Length == startDelimeterLength ||
377-
(token.Span.Length > startDelimeterLength && token.Text[^1] != lastChar);
375+
var startDelimiterLength = token.IsVerbatimStringLiteral() ? 2 : 1;
376+
return token.Span.Length == startDelimiterLength ||
377+
(token.Span.Length > startDelimiterLength && token.Text[^1] != lastChar);
378378
}
379379
}
380380

0 commit comments

Comments
 (0)