Move indentation service to VS.Editor.Razor and export it.
- Removed indentation service extensions and made `GetDesiredIndentation` understand `ITextSnapshot`s and `ITextLineSnapshot`. - Updated our test `StringTextSnapshot` implementation to have the required features our indentation tests require. - Updated indentation service tests to use new API. #1762
This commit is contained in:
parent
44a47182b2
commit
de23788019
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Composition;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
[Shared]
|
||||
[ExportLanguageServiceFactory(typeof(RazorIndentationFactsService), RazorLanguage.Name, ServiceLayer.Default)]
|
||||
internal class DefaultRazorIndentationFactsServiceFactory : ILanguageServiceFactory
|
||||
{
|
||||
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
|
||||
{
|
||||
return new DefaultRazorIndentationFactsService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,28 +3,38 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Span = Microsoft.AspNetCore.Razor.Language.Legacy.Span;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
[System.Composition.Shared]
|
||||
[Export(typeof(RazorIndentationFactsService))]
|
||||
internal class DefaultRazorIndentationFactsService : RazorIndentationFactsService
|
||||
{
|
||||
public override int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEndIndex, Func<int, string> getLineContent, int indentSize, int tabSize)
|
||||
public override int? GetDesiredIndentation(
|
||||
RazorSyntaxTree syntaxTree,
|
||||
ITextSnapshot syntaxTreeSnapshot,
|
||||
ITextSnapshotLine line,
|
||||
int indentSize,
|
||||
int tabSize)
|
||||
{
|
||||
if (syntaxTree == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(syntaxTree));
|
||||
}
|
||||
|
||||
if (previousLineEndIndex < 0)
|
||||
if (syntaxTreeSnapshot == null)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(previousLineEndIndex));
|
||||
throw new ArgumentNullException(nameof(syntaxTreeSnapshot));
|
||||
}
|
||||
|
||||
if (getLineContent == null)
|
||||
if (line == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(getLineContent));
|
||||
throw new ArgumentNullException(nameof(line));
|
||||
}
|
||||
|
||||
if (indentSize < 0)
|
||||
|
|
@ -37,6 +47,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
throw new ArgumentOutOfRangeException(nameof(tabSize));
|
||||
}
|
||||
|
||||
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
||||
var trackingPoint = previousLine.Snapshot.CreateTrackingPoint(previousLine.End, PointTrackingMode.Negative);
|
||||
var previousLineEndIndex = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
||||
|
||||
var simulatedChange = new SourceChange(previousLineEndIndex, 0, string.Empty);
|
||||
var owningSpan = LocateOwner(syntaxTree.Root, simulatedChange);
|
||||
if (owningSpan.Kind == SpanKindInternal.Code)
|
||||
|
|
@ -80,8 +94,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
// We can't rely on the syntax trees representation of the source document because partial parses may have mutated
|
||||
// the underlying SyntaxTree text buffer. Because of this, if we want to provide accurate indentations we need to
|
||||
// operate on the current line representation as indicated by the provider.
|
||||
var line = getLineContent(currentSpan.Start.LineIndex);
|
||||
desiredIndentation = GetIndentLevelOfLine(line, tabSize) + indentSize;
|
||||
var lineText = line.Snapshot.GetLineFromLineNumber(currentSpan.Start.LineIndex).GetText();
|
||||
desiredIndentation = GetIndentLevelOfLine(lineText, tabSize) + indentSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
public abstract class RazorIndentationFactsService : ILanguageService
|
||||
{
|
||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, int previousLineEnd, Func<int, string> lineProvider, int indentSize, int tabSize);
|
||||
public abstract int? GetDesiredIndentation(RazorSyntaxTree syntaxTree, ITextSnapshot syntaxTreeSnapshot, ITextSnapshotLine line, int indentSize, int tabSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public static class RazorIndentationFactsServiceExtensions
|
||||
{
|
||||
public static int? GetDesiredIndentation(
|
||||
this RazorIndentationFactsService service,
|
||||
RazorSyntaxTree syntaxTree,
|
||||
ITextSnapshot syntaxTreeSnapshot,
|
||||
ITextSnapshotLine line,
|
||||
int indentSize,
|
||||
int tabSize)
|
||||
{
|
||||
// The tricky thing here is that line.Snapshot is very likely newer
|
||||
var previousLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
|
||||
var trackingPoint = line.Snapshot.CreateTrackingPoint(line.End, PointTrackingMode.Negative);
|
||||
var previousLineEnd = trackingPoint.GetPosition(syntaxTreeSnapshot);
|
||||
|
||||
Func<int, string> getLineContentDelegate = (lineIndex) => line.Snapshot.GetLineFromLineNumber(lineIndex).GetText();
|
||||
|
||||
return service.GetDesiredIndentation(syntaxTree, previousLineEnd, getLineContentDelegate, indentSize, tabSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +77,20 @@ namespace Microsoft.VisualStudio.Text
|
|||
return matchingLine;
|
||||
}
|
||||
|
||||
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode) => throw new NotImplementedException();
|
||||
public ITextSnapshotLine GetLineFromLineNumber(int lineNumber)
|
||||
{
|
||||
if (lineNumber < 0 || lineNumber >= _lines.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(lineNumber));
|
||||
}
|
||||
|
||||
return _lines[lineNumber];
|
||||
}
|
||||
|
||||
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode)
|
||||
{
|
||||
return new SnapshotTrackingPoint(position);
|
||||
}
|
||||
|
||||
public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
||||
|
||||
|
|
@ -89,8 +102,6 @@ namespace Microsoft.VisualStudio.Text
|
|||
|
||||
public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) => throw new NotImplementedException();
|
||||
|
||||
public ITextSnapshotLine GetLineFromLineNumber(int lineNumber) => throw new NotImplementedException();
|
||||
|
||||
public int GetLineNumberFromPosition(int position) => throw new NotImplementedException();
|
||||
|
||||
public string GetText(VisualStudio.Text.Span span) => throw new NotImplementedException();
|
||||
|
|
@ -133,6 +144,30 @@ namespace Microsoft.VisualStudio.Text
|
|||
}
|
||||
}
|
||||
|
||||
private class SnapshotTrackingPoint : ITrackingPoint
|
||||
{
|
||||
private readonly int _position;
|
||||
|
||||
public SnapshotTrackingPoint(int position)
|
||||
{
|
||||
_position = position;
|
||||
}
|
||||
|
||||
public ITextBuffer TextBuffer => throw new NotImplementedException();
|
||||
|
||||
public PointTrackingMode TrackingMode => throw new NotImplementedException();
|
||||
|
||||
public TrackingFidelityMode TrackingFidelity => throw new NotImplementedException();
|
||||
|
||||
public char GetCharacter(ITextSnapshot snapshot) => throw new NotImplementedException();
|
||||
|
||||
public SnapshotPoint GetPoint(ITextSnapshot snapshot) => throw new NotImplementedException();
|
||||
|
||||
public int GetPosition(ITextSnapshot snapshot) => _position;
|
||||
|
||||
public int GetPosition(ITextVersion version) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SnapshotLine : ITextSnapshotLine
|
||||
{
|
||||
private readonly string _contentWithLineBreak;
|
||||
|
|
@ -153,7 +188,9 @@ namespace Microsoft.VisualStudio.Text
|
|||
}
|
||||
|
||||
Start = new SnapshotPoint(owner, start);
|
||||
End = new SnapshotPoint(owner, start + _content.Length);
|
||||
Snapshot = owner;
|
||||
LineNumber = (owner as StringTextSnapshot)._lines.Count;
|
||||
}
|
||||
|
||||
public ITextSnapshot Snapshot { get; }
|
||||
|
|
@ -172,14 +209,14 @@ namespace Microsoft.VisualStudio.Text
|
|||
|
||||
public string GetTextIncludingLineBreak() => _contentWithLineBreak;
|
||||
|
||||
public int LineNumber => throw new NotImplementedException();
|
||||
public int LineNumber { get; }
|
||||
|
||||
public SnapshotPoint End { get; }
|
||||
|
||||
public SnapshotSpan Extent => throw new NotImplementedException();
|
||||
|
||||
public SnapshotSpan ExtentIncludingLineBreak => throw new NotImplementedException();
|
||||
|
||||
public SnapshotPoint End => throw new NotImplementedException();
|
||||
|
||||
public SnapshotPoint EndIncludingLineBreak => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. 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.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
namespace Microsoft.VisualStudio.Editor.Razor
|
||||
{
|
||||
public class DefaultRazorIndentationFactsServiceTest
|
||||
{
|
||||
|
|
@ -15,17 +15,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void GetDesiredIndentation_ReturnsNull_IfOwningSpanIsCode()
|
||||
{
|
||||
// Arrange
|
||||
var source = $@"
|
||||
var source = new StringTextSnapshot($@"
|
||||
@{{
|
||||
";
|
||||
");
|
||||
var syntaxTree = GetSyntaxTree(source);
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(2),
|
||||
indentSize: 4,
|
||||
tabSize: 1);
|
||||
|
||||
|
|
@ -38,17 +38,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateSingleLineDirective("custom");
|
||||
var source = $@"
|
||||
var source = new StringTextSnapshot($@"
|
||||
@custom
|
||||
";
|
||||
");
|
||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(2),
|
||||
indentSize: 4,
|
||||
tabSize: 1);
|
||||
|
||||
|
|
@ -60,17 +60,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForMarkupWithinCodeBlock()
|
||||
{
|
||||
// Arrange
|
||||
var source = $@"@{{
|
||||
var source = new StringTextSnapshot($@"@{{
|
||||
<div>
|
||||
";
|
||||
");
|
||||
var syntaxTree = GetSyntaxTree(source);
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 1),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(2),
|
||||
indentSize: 4,
|
||||
tabSize: 4);
|
||||
|
||||
|
|
@ -83,18 +83,18 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||
var source = $@"@custom
|
||||
var source = new StringTextSnapshot($@"@custom
|
||||
{{
|
||||
<div>
|
||||
}}";
|
||||
}}");
|
||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 2),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(3),
|
||||
indentSize: 4,
|
||||
tabSize: 4);
|
||||
|
||||
|
|
@ -106,21 +106,21 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public void GetDesiredIndentation_ReturnsCorrectIndentation_ForNestedMarkupWithinCodeBlock()
|
||||
{
|
||||
// Arrange
|
||||
var source = $@"
|
||||
var source = new StringTextSnapshot($@"
|
||||
<div>
|
||||
@{{
|
||||
<span>
|
||||
}}
|
||||
</div>
|
||||
";
|
||||
");
|
||||
var syntaxTree = GetSyntaxTree(source);
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(4),
|
||||
indentSize: 4,
|
||||
tabSize: 4);
|
||||
|
||||
|
|
@ -133,20 +133,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
// Arrange
|
||||
var customDirective = DirectiveDescriptor.CreateRazorBlockDirective("custom");
|
||||
var source = $@"@custom
|
||||
var source = new StringTextSnapshot($@"@custom
|
||||
{{
|
||||
@{{
|
||||
<div>
|
||||
}}
|
||||
}}";
|
||||
}}");
|
||||
var syntaxTree = GetSyntaxTree(source, new[] { customDirective });
|
||||
var service = new DefaultRazorIndentationFactsService();
|
||||
|
||||
// Act
|
||||
var indentation = service.GetDesiredIndentation(
|
||||
syntaxTree,
|
||||
previousLineEndIndex: GetLineEndIndexForLine(source, 3),
|
||||
getLineContent: line => GetLineContent(source, line),
|
||||
source,
|
||||
source.GetLineFromLineNumber(4),
|
||||
indentSize: 4,
|
||||
tabSize: 4);
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
Assert.Equal(8, indentation);
|
||||
}
|
||||
|
||||
private static RazorSyntaxTree GetSyntaxTree(string source, IEnumerable<DirectiveDescriptor> directives = null)
|
||||
private static RazorSyntaxTree GetSyntaxTree(StringTextSnapshot source, IEnumerable<DirectiveDescriptor> directives = null)
|
||||
{
|
||||
directives = directives ?? Enumerable.Empty<DirectiveDescriptor>();
|
||||
var engine = RazorEngine.CreateDesignTime(builder =>
|
||||
|
|
@ -165,45 +165,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
});
|
||||
|
||||
var sourceDocument = RazorSourceDocument.Create(source, "test.cshtml");
|
||||
var sourceDocument = RazorSourceDocument.Create(source.GetText(), "test.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||
|
||||
engine.Process(codeDocument);
|
||||
|
||||
return codeDocument.GetSyntaxTree();
|
||||
}
|
||||
|
||||
private static string GetLineContent(string source, int lineIndex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
return lines[lineIndex];
|
||||
}
|
||||
|
||||
private static int GetLineEndIndexForLine(string source, int lineIndex)
|
||||
{
|
||||
var absoluteIndex = 0;
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
return absoluteIndex;
|
||||
}
|
||||
|
||||
var lines = source.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
for (var i = 0; i <= lineIndex; i++)
|
||||
{
|
||||
absoluteIndex += lines[i].Length;
|
||||
|
||||
if (i < lineIndex)
|
||||
{
|
||||
absoluteIndex += Environment.NewLine.Length;
|
||||
}
|
||||
}
|
||||
|
||||
return absoluteIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue