private static List <TextChange> CleanupDocument(FormattingContext context, Range?range = null) { var isOnType = range is not null; var text = context.SourceText; range ??= TextSpan.FromBounds(0, text.Length).AsRange(text); var csharpDocument = context.CodeDocument.GetCSharpDocument(); var changes = new List <TextChange>(); foreach (var mapping in csharpDocument.SourceMappings) { var mappingSpan = new TextSpan(mapping.OriginalSpan.AbsoluteIndex, mapping.OriginalSpan.Length); var mappingRange = mappingSpan.AsRange(text); if (!range.LineOverlapsWith(mappingRange)) { // We don't care about this range. It didn't change. continue; } CleanupSourceMappingStart(context, mappingRange, changes, isOnType, out var newLineAdded); CleanupSourceMappingEnd(context, mappingRange, changes, newLineAdded); } return(changes); }
public static Range AsLSPRange(this TextSpan span, SourceText sourceText) { var range = span.AsRange(sourceText); return(new Range() { Start = new Position((int)range.Start.Line, (int)range.Start.Character), End = new Position((int)range.End.Line, (int)range.End.Character) }); }
public async Task MapSpans_WithinRange_ReturnsMapping() { // Arrange var called = false; var textSpan = new TextSpan(1, 10); var spans = new TextSpan[] { textSpan }; var documentSnapshot = new Mock <LSPDocumentSnapshot>(MockBehavior.Strict); documentSnapshot.SetupGet(doc => doc.Uri).Returns(_mockDocumentUri); var textSnapshot = new StringTextSnapshot(s_mockGeneratedContent, 1); var textSpanAsRange = textSpan.AsRange(_sourceTextGenerated); var mappedRange = new Range() { Start = new Position(2, 1), End = new Position(2, 11) }; var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict); var mappingResult = new RazorMapToDocumentRangesResponse() { Ranges = new Range[] { mappedRange } }; documentMappingProvider.Setup(dmp => dmp.MapToDocumentRangesAsync(It.IsAny <RazorLanguageKind>(), It.IsAny <Uri>(), It.IsAny <Range[]>(), It.IsAny <CancellationToken>())) .Callback <RazorLanguageKind, Uri, Range[], CancellationToken>((languageKind, uri, ranges, ct) => { Assert.Equal(RazorLanguageKind.CSharp, languageKind); Assert.Equal(_mockDocumentUri, uri); Assert.Single(ranges, textSpanAsRange); called = true; }) .Returns(Task.FromResult(mappingResult)); var service = new RazorLSPSpanMappingService(documentMappingProvider.Object, documentSnapshot.Object, textSnapshot); var expectedSpan = mappedRange.AsTextSpan(_sourceTextRazor); var expectedLinePosition = _sourceTextRazor.Lines.GetLinePositionSpan(expectedSpan); var expectedFilePath = _mockDocumentUri.LocalPath; var expectedResult = (expectedFilePath, expectedLinePosition, expectedSpan); // Act var result = await service.MapSpansAsyncTest(spans, _sourceTextGenerated, _sourceTextRazor).ConfigureAwait(false); // Assert Assert.True(called); Assert.Single(result, expectedResult); }
public async Task MapSpans_OutsideRange_ReturnsEmpty() { // Arrange var called = false; var textSpan = new TextSpan(10, 10); var spans = new TextSpan[] { textSpan }; var documentSnapshot = new Mock <LSPDocumentSnapshot>(MockBehavior.Strict); documentSnapshot.SetupGet(doc => doc.Uri).Returns(_mockDocumentUri); var textSnapshot = new StringTextSnapshot(s_mockGeneratedContent, 1); var textSpanAsRange = textSpan.AsRange(_sourceTextGenerated); var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict); documentMappingProvider.Setup(dmp => dmp.MapToDocumentRangesAsync(It.IsAny <RazorLanguageKind>(), It.IsAny <Uri>(), It.IsAny <Range[]>(), It.IsAny <CancellationToken>())) .Callback <RazorLanguageKind, Uri, Range[], CancellationToken>((languageKind, uri, ranges, ct) => { Assert.Equal(RazorLanguageKind.CSharp, languageKind); Assert.Equal(_mockDocumentUri, uri); Assert.Single(ranges, textSpanAsRange); called = true; }) .Returns(Task.FromResult <RazorMapToDocumentRangesResponse>(null)); var service = new RazorLSPSpanMappingService(documentMappingProvider.Object, documentSnapshot.Object, textSnapshot); // Act var result = await service.MapSpansAsyncTest(spans, _sourceTextGenerated, _sourceTextRazor).ConfigureAwait(false); // Assert Assert.True(called); Assert.Empty(result); }
private SourceText CleanupDocument(FormattingContext context, Range range = null) { // // We look through every source mapping that intersects with the affected range and // adjust the indentation of the first line, // // E.g, // // @{ public int x = 0; // } // // becomes, // // @{ // public int x = 0; // } // var text = context.SourceText; range ??= TextSpan.FromBounds(0, text.Length).AsRange(text); var csharpDocument = context.CodeDocument.GetCSharpDocument(); var changes = new List <TextChange>(); foreach (var mapping in csharpDocument.SourceMappings) { var mappingSpan = new TextSpan(mapping.OriginalSpan.AbsoluteIndex, mapping.OriginalSpan.Length); var mappingRange = mappingSpan.AsRange(text); if (!range.LineOverlapsWith(mappingRange)) { // We don't care about this range. It didn't change. continue; } var mappingStartLineIndex = (int)mappingRange.Start.Line; if (context.Indentations[mappingStartLineIndex].StartsInCSharpContext) { // Doesn't need cleaning up. // For corner cases like (Range marked with |...|), // @{ // if (true} { <div></div>| }| // } // We want to leave it alone because tackling it here is really complicated. continue; } // @{ // if (true) // { // <div></div>| // // |} // } // We want to return the length of the range marked by |...| // var whitespaceLength = text.GetFirstNonWhitespaceOffset(mappingSpan); if (whitespaceLength == null) { // There was no content here. Skip. continue; } var spanToReplace = new TextSpan(mappingSpan.Start, whitespaceLength.Value); if (!context.TryGetIndentationLevel(spanToReplace.End, out var contentIndentLevel)) { // Can't find the correct indentation for this content. Leave it alone. continue; } // At this point, `contentIndentLevel` should contain the correct indentation level for `}` in the above example. var replacement = context.NewLineString + context.GetIndentationLevelString(contentIndentLevel); // After the below change the above example should look like, // @{ // if (true) // { // <div></div> // } // } var change = new TextChange(spanToReplace, replacement); changes.Add(change); } var changedText = text.WithChanges(changes); return(changedText); }