示例#1
0
        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);
        }
示例#2
0
        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)
            });
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#5
0
        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);
        }