未验证 提交 dfb58d79 编写于 作者: A Allison Chou 提交者: GitHub

Merge pull request #39554 from allisonchou/MultiCaretBug

Fix for split string literals broken with multi-carets
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Formatting;
......@@ -56,22 +56,43 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args)
var spans = textView.Selection.GetSnapshotSpansOnBuffer(subjectBuffer);
// Don't split strings if there is any actual selection.
if (spans.Count == 1 && spans[0].IsEmpty)
// We must check all spans to account for multi-carets.
if (spans.IsEmpty() || !spans.All(s => s.IsEmpty))
{
var caret = textView.GetCaretPoint(subjectBuffer);
if (caret != null)
return false;
}
var caret = textView.GetCaretPoint(subjectBuffer);
if (caret == null)
{
return false;
}
// First, we need to verify that we are only working with string literals.
// Otherwise, let the editor handle all carets.
foreach (var span in spans)
{
var spanStart = span.Start;
var line = subjectBuffer.CurrentSnapshot.GetLineFromPosition(span.Start);
if (!LineContainsQuote(line, span.Start))
{
// Quick check. If the line doesn't contain a quote in it before the caret,
// then no point in doing any more expensive synchronous work.
var line = subjectBuffer.CurrentSnapshot.GetLineFromPosition(caret.Value);
if (LineContainsQuote(line, caret.Value))
{
return SplitString(textView, subjectBuffer, caret.Value);
}
return false;
}
}
return false;
// We now go through the verified string literals and split each of them.
// The list of spans is traversed in reverse order so we do not have to
// deal with updating later caret positions to account for the added space
// from splitting at earlier caret positions.
foreach (var span in spans.Reverse())
{
if (!SplitString(textView, subjectBuffer, span.Start))
{
return false;
}
}
return true;
}
private bool SplitString(ITextView textView, ITextBuffer subjectBuffer, SnapshotPoint caret)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Editor.CSharp.SplitStringLiteral;
......@@ -10,6 +11,7 @@
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;
using Microsoft.VisualStudio.Text.Operations;
using Roslyn.Test.Utilities;
......@@ -41,8 +43,14 @@ public class SplitStringLiteralCommandHandlerTests
var view = document.GetTextView();
var originalSnapshot = view.TextBuffer.CurrentSnapshot;
var originalSelection = document.SelectedSpans.Single();
view.SetSelection(originalSelection.ToSnapshotSpan(originalSnapshot));
var originalSelections = document.SelectedSpans;
var snapshotSpans = new List<SnapshotSpan>();
foreach (var selection in originalSelections)
{
snapshotSpans.Add(selection.ToSnapshotSpan(originalSnapshot));
}
view.SetMultiSelection(snapshotSpans);
var undoHistoryRegistry = workspace.GetService<ITextUndoHistoryRegistry>();
var commandHandler = new SplitStringLiteralCommandHandler(
......@@ -60,17 +68,17 @@ public class SplitStringLiteralCommandHandlerTests
out var expectedOutput, out ImmutableArray<TextSpan> expectedSpans);
Assert.Equal(expectedOutput, view.TextBuffer.CurrentSnapshot.AsText().ToString());
Assert.Equal(expectedSpans.Single().Start, view.Caret.Position.BufferPosition.Position);
Assert.Equal(expectedSpans.First().Start, view.Caret.Position.BufferPosition.Position);
if (verifyUndo)
{
// Ensure that after undo we go back to where we were to begin with.
var history = undoHistoryRegistry.GetHistory(document.GetTextBuffer());
history.Undo(count: 1);
history.Undo(count: originalSelections.Count);
var currentSnapshot = document.GetTextBuffer().CurrentSnapshot;
Assert.Equal(originalSnapshot.GetText(), currentSnapshot.GetText());
Assert.Equal(originalSelection.Start, view.Caret.Position.BufferPosition.Position);
Assert.Equal(originalSelections.First().Start, view.Caret.Position.BufferPosition.Position);
}
}
}
......@@ -744,5 +752,85 @@ static void Main(string[] args)
}",
verifyUndo: false);
}
[WorkItem(39040, "https://github.com/dotnet/roslyn/issues/39040")]
[WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)]
public void TestMultiCaretSingleLine()
{
TestHandled(
@"class C
{
void M()
{
var v = ""now is [||]the ti[||]me"";
}
}",
@"class C
{
void M()
{
var v = ""now is "" +
""[||]the ti"" +
""[||]me"";
}
}");
}
[WorkItem(39040, "https://github.com/dotnet/roslyn/issues/39040")]
[WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)]
public void TestMultiCaretMultiLines()
{
TestHandled(
@"class C
{
string s = ""hello w[||]orld"";
void M()
{
var v = ""now is [||]the ti[||]me"";
}
}",
@"class C
{
string s = ""hello w"" +
""[||]orld"";
void M()
{
var v = ""now is "" +
""[||]the ti"" +
""[||]me"";
}
}");
}
[WorkItem(39040, "https://github.com/dotnet/roslyn/issues/39040")]
[WpfFact, Trait(Traits.Feature, Traits.Features.SplitStringLiteral)]
public void TestMultiCaretInterpolatedString()
{
TestHandled(
@"class C
{
string s = ""hello w[||]orld"";
void M()
{
var location = ""world"";
var s = $""H[||]ello {location}!"";
}
}",
@"class C
{
string s = ""hello w"" +
""[||]orld"";
void M()
{
var location = ""world"";
var s = $""H"" +
$""[||]ello {location}!"";
}
}");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册