提交 cf24e4d5 编写于 作者: D David Poeschl

Merge pull request #4913 from dpoeschl/SnippetsWithTabs

Calculate snippet $end$ location based on column, not line length
......@@ -10,10 +10,11 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
......@@ -25,7 +26,6 @@
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using MSXML;
using Roslyn.Utilities;
using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets
......@@ -169,7 +169,20 @@ private void CleanUpEndLocation(ITrackingSpan endTrackingSpan)
if (lineText.Trim() == string.Empty)
{
indentCaretOnCommit = true;
indentDepth = lineText.Length;
var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
var optionService = document.Project.Solution.Workspace.Services.GetService<IOptionService>();
var tabSize = optionService.GetOption(FormattingOptions.TabSize, document.Project.Language);
indentDepth = lineText.GetColumnFromLineOffset(lineText.Length, tabSize);
}
else
{
// If we don't have a document, then just guess the typical default TabSize value.
indentDepth = lineText.GetColumnFromLineOffset(lineText.Length, tabSize: 4);
}
SubjectBuffer.Delete(new Span(line.Start.Position, line.Length));
endSnapshotSpan = SubjectBuffer.CurrentSnapshot.GetSpan(new Span(line.Start.Position, 0));
}
......@@ -259,25 +272,35 @@ public int PositionCaretForEditing(IVsTextLines pBuffer, [ComAliasName("Microsof
// and the navigation location will be at column 0 on a blank line. We must now
// position the caret in virtual space.
if (indentCaretOnCommit)
{
int lineLength;
pBuffer.GetLengthOfLine(ts[0].iStartLine, out lineLength);
int lineLength;
pBuffer.GetLengthOfLine(ts[0].iStartLine, out lineLength);
string lineText;
pBuffer.GetLineText(ts[0].iStartLine, 0, ts[0].iStartLine, lineLength, out lineText);
string endLineText;
pBuffer.GetLineText(ts[0].iStartLine, 0, ts[0].iStartLine, lineLength, out endLineText);
if (lineText == string.Empty)
{
int endLinePosition;
pBuffer.GetPositionOfLine(ts[0].iStartLine, out endLinePosition);
TextView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(TextView.TextSnapshot.GetPoint(endLinePosition), indentDepth));
}
}
int endLinePosition;
pBuffer.GetPositionOfLine(ts[0].iStartLine, out endLinePosition);
PositionCaretForEditingInternal(endLineText, endLinePosition);
return VSConstants.S_OK;
}
/// <summary>
/// Internal for testing purposes. All real caret positioning logic takes place here. <see cref="PositionCaretForEditing"/>
/// only extracts the <paramref name="endLineText"/> and <paramref name="endLinePosition"/> from the provided <see cref="IVsTextLines"/>.
/// Tests can call this method directly to avoid producing an IVsTextLines.
/// </summary>
/// <param name="endLineText"></param>
/// <param name="endLinePosition"></param>
internal void PositionCaretForEditingInternal(string endLineText, int endLinePosition)
{
if (indentCaretOnCommit && endLineText == string.Empty)
{
TextView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(TextView.TextSnapshot.GetPoint(endLinePosition), indentDepth));
}
}
public virtual bool TryHandleTab()
{
if (ExpansionSession != null)
......
......@@ -4,6 +4,8 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.VisualStudio.LanguageServices
Imports Microsoft.VisualStudio.LanguageServices.CSharp.Snippets
Imports Microsoft.VisualStudio.Text.Projection
......@@ -163,7 +165,7 @@ using G= H.I;
}
&lt;/div&gt;</SurfaceBuffer>
Test(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
TestProjectionFormatting(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.Snippets)>
......@@ -193,7 +195,7 @@ using G= H.I;
}
&lt;/div&gt;</SurfaceBuffer>
Test(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
TestProjectionFormatting(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.Snippets)>
......@@ -223,10 +225,74 @@ using G= H.I;
}
&lt;/div&gt;</SurfaceBuffer>
Test(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
TestProjectionFormatting(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Sub
Public Sub Test(workspaceXmlWithSubjectBufferDocument As XElement, surfaceBufferDocumentXml As XElement, expectedSurfaceBuffer As XElement)
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_3()
TestFormattingWithTabSize(3)
End Sub
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_4()
TestFormattingWithTabSize(4)
End Sub
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_5()
TestFormattingWithTabSize(5)
End Sub
Public Sub TestFormattingWithTabSize(tabSize As Integer)
Dim workspaceXml =
<Workspace>
<Project Language=<%= LanguageNames.CSharp %> CommonReferences="true">
<Document>class C {
void M()
{
[|for (int x = 0; x &lt; length; x++)
{
$$
}|]
}
}</Document>
</Project>
</Workspace>
Dim expectedResult = <Test>class C {
void M()
{
for (int x = 0; x &lt; length; x++)
{
}
}
}</Test>
Using testWorkspace = TestWorkspaceFactory.CreateWorkspace(workspaceXml)
Dim document = testWorkspace.Documents.Single()
Dim optionService = testWorkspace.Services.GetService(Of IOptionService)()
Dim optionSet = optionService.GetOptions()
optionSet = optionSet.WithChangedOption(FormattingOptions.UseTabs, document.Project.Language, True)
optionSet = optionSet.WithChangedOption(FormattingOptions.TabSize, document.Project.Language, tabSize)
optionSet = optionSet.WithChangedOption(FormattingOptions.IndentationSize, document.Project.Language, tabSize)
optionService.SetOptions(optionSet)
Dim snippetExpansionClient = New SnippetExpansionClient(
Guids.CSharpLanguageServiceId,
document.GetTextView(),
document.TextBuffer,
Nothing)
SnippetExpansionClientTestsHelper.TestFormattingAndCaretPosition(snippetExpansionClient, document, expectedResult, tabSize * 3)
End Using
End Sub
Public Sub TestProjectionFormatting(workspaceXmlWithSubjectBufferDocument As XElement, surfaceBufferDocumentXml As XElement, expectedSurfaceBuffer As XElement)
Using testWorkspace = TestWorkspaceFactory.CreateWorkspace(workspaceXmlWithSubjectBufferDocument)
Dim subjectBufferDocument = testWorkspace.Documents.Single()
......@@ -242,7 +308,7 @@ using G= H.I;
subjectBufferDocument.TextBuffer,
Nothing)
SnippetExpansionClientTestsHelper.Test(snippetExpansionClient, subjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
SnippetExpansionClientTestsHelper.TestProjectionBuffer(snippetExpansionClient, subjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Using
End Sub
......
......@@ -7,7 +7,7 @@ Imports Microsoft.VisualStudio.TextManager.Interop
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets
Friend Class SnippetExpansionClientTestsHelper
Public Shared Sub Test(snippetExpansionClient As AbstractSnippetExpansionClient,
Public Shared Sub TestProjectionBuffer(snippetExpansionClient As AbstractSnippetExpansionClient,
subjectBufferDocument As TestHostDocument,
surfaceBufferDocument As TestHostDocument,
expectedSurfaceBuffer As XElement)
......@@ -44,5 +44,63 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Snippets
Assert.Equal(expectedSurfaceBuffer.NormalizedValue, surfaceBufferDocument.TextBuffer.CurrentSnapshot.GetText)
End Sub
Friend Shared Sub TestFormattingAndCaretPosition(
snippetExpansionClient As AbstractSnippetExpansionClient,
document As TestHostDocument,
expectedResult As XElement,
expectedVirtualSpacing As Integer)
Dim mockExpansionSession = New TestExpansionSession()
Dim cursorPosition = document.CursorPosition.Value
Dim cursorLine = document.TextBuffer.CurrentSnapshot.GetLineFromPosition(cursorPosition)
mockExpansionSession.SetEndSpan(New TextSpan() With
{
.iStartLine = cursorLine.LineNumber,
.iStartIndex = cursorPosition - cursorLine.Start.Position,
.iEndLine = cursorLine.LineNumber,
.iEndIndex = cursorPosition - cursorLine.Start.Position
})
snippetExpansionClient.OnBeforeInsertion(mockExpansionSession)
Dim snippetSpan = document.SelectedSpans(0)
Dim snippetStartLine = document.TextBuffer.CurrentSnapshot.GetLineFromPosition(snippetSpan.Start)
Dim snippetEndLine = document.TextBuffer.CurrentSnapshot.GetLineFromPosition(snippetSpan.End)
Dim snippetTextSpan = New TextSpan() With
{
.iStartLine = snippetStartLine.LineNumber,
.iStartIndex = snippetSpan.Start - snippetStartLine.Start.Position,
.iEndLine = snippetEndLine.LineNumber,
.iEndIndex = snippetSpan.End - snippetEndLine.Start.Position
}
mockExpansionSession.snippetSpanInSurfaceBuffer = snippetTextSpan
Dim endPosition = document.CursorPosition.Value
Dim endPositionLine = document.TextBuffer.CurrentSnapshot.GetLineFromPosition(endPosition)
Dim endPositionIndex = endPosition - endPositionLine.Start.Position
mockExpansionSession.endSpanInSurfaceBuffer = New TextSpan() With
{
.iStartLine = endPositionLine.LineNumber,
.iStartIndex = endPositionIndex,
.iEndLine = endPositionLine.LineNumber,
.iEndIndex = endPositionIndex
}
snippetExpansionClient.FormatSpan(Nothing, {snippetTextSpan})
Dim finalEndSpan(1) As TextSpan
mockExpansionSession.GetEndSpan(finalEndSpan)
Dim formattedEndPositionLine = document.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(finalEndSpan(0).iStartLine)
snippetExpansionClient.PositionCaretForEditingInternal(formattedEndPositionLine.GetText(), formattedEndPositionLine.Start.Position)
Assert.Equal(expectedVirtualSpacing, document.GetTextView().Caret.Position.VirtualSpaces)
Assert.Equal(expectedResult.NormalizedValue, document.TextBuffer.CurrentSnapshot.GetText)
End Sub
End Class
End Namespace
......@@ -4,7 +4,8 @@ Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.VisualStudio.LanguageServices
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets
Imports Microsoft.VisualStudio.Text.Projection
Imports Roslyn.Test.Utilities
......@@ -310,6 +311,66 @@ Next
TestFormatting(workspaceXmlWithSubjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Sub
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_3()
TestFormattingWithTabSize(3)
End Sub
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_4()
TestFormattingWithTabSize(4)
End Sub
<Fact, WorkItem(4652, "https://github.com/dotnet/roslyn/issues/4652")>
<Trait(Traits.Feature, Traits.Features.Snippets)>
Public Sub SnippetFormatting_TabSize_5()
TestFormattingWithTabSize(5)
End Sub
Public Sub TestFormattingWithTabSize(tabSize As Integer)
Dim workspaceXml =
<Workspace>
<Project Language=<%= LanguageNames.VisualBasic %> CommonReferences="true">
<Document>Class C
Sub M()
[|For index = 1 To 10
$$
Next|]
End Sub
End Class</Document>
</Project>
</Workspace>
Dim expectedResult = <Test>Class C
Sub M()
For index = 1 To 10
Next
End Sub
End Class</Test>
Using testWorkspace = TestWorkspaceFactory.CreateWorkspace(workspaceXml)
Dim document = testWorkspace.Documents.Single()
Dim optionService = testWorkspace.Services.GetService(Of IOptionService)()
Dim optionSet = optionService.GetOptions()
optionSet = optionSet.WithChangedOption(FormattingOptions.UseTabs, document.Project.Language, True)
optionSet = optionSet.WithChangedOption(FormattingOptions.TabSize, document.Project.Language, tabSize)
optionSet = optionSet.WithChangedOption(FormattingOptions.IndentationSize, document.Project.Language, tabSize)
optionService.SetOptions(optionSet)
Dim snippetExpansionClient = New SnippetExpansionClient(
Guids.CSharpLanguageServiceId,
document.GetTextView(),
document.TextBuffer,
Nothing)
SnippetExpansionClientTestsHelper.TestFormattingAndCaretPosition(snippetExpansionClient, document, expectedResult, tabSize * 3)
End Using
End Sub
Private Sub TestSnippetAddImports(originalCode As String, namespacesToAdd As String(), placeSystemNamespaceFirst As Boolean, expectedUpdatedCode As String)
Dim workspaceXml = <Workspace>
<Project Language=<%= LanguageNames.VisualBasic %> CommonReferences="true">
......@@ -362,7 +423,7 @@ Next
subjectBufferDocument.TextBuffer,
Nothing)
SnippetExpansionClientTestsHelper.Test(snippetExpansionClient, subjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
SnippetExpansionClientTestsHelper.TestProjectionBuffer(snippetExpansionClient, subjectBufferDocument, surfaceBufferDocument, expectedSurfaceBuffer)
End Using
End Sub
End Class
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册