Ejemplo n.º 1
0
        // private utils related to extracting file content

        /// <summary>
        /// Checks that the given range is a valid range in file, and returns the text in the given range in concatenated form
        /// stripping (only) end of line comments (and not removing excess brackets).
        /// Note: the End position of the given range is *not* part of the returned string.
        /// </summary>
        private static string GetCodeSnippet(this FileContentManager file, LSP.Range range)
        {
            if (!Utils.IsValidRange(range, file))
            {
                throw new ArgumentException($"cannot extract code snippet for the given range \n range: {range.DiagnosticString()}");
            }
            string CodeLine(CodeLine line) => line.WithoutEnding + line.LineEnding;

            var start = range.Start.Line;
            var count = range.End.Line - start + 1;

            var firstLine = CodeLine(file.GetLine(start));

            if (count == 1)
            {
                return(firstLine.Substring(range.Start.Character, range.End.Character - range.Start.Character));
            }

            var lastLine = CodeLine(file.GetLine(range.End.Line));
            var prepend  = firstLine.Substring(range.Start.Character);
            var append   = lastLine.Substring(0, range.End.Character);

            var middle = file.GetLines(start + 1, count - 2).Select(CodeLine).ToArray();

            if (middle.Length == 0)
            {
                return(Utils.JoinLines(new string[] { prepend, append }));
            }
            else
            {
                return(Utils.JoinLines(new string[] { prepend, Utils.JoinLines(middle), append })); // Note: use JoinLines here to get accurate position infos for errors
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Extracts the code fragments based on the current file content that need to be re-processed due to content changes on the given lines.
        /// Ignores any whitespace or comments at the beginning of the file (whether they have changed or not).
        /// Ignores any whitespace or comments that occur after the last piece of code in the file.
        /// Throws an ArgumentNullException if any of the arguments is null.
        /// </summary>
        private static IEnumerable <CodeFragment> FragmentsToProcess(this FileContentManager file, SortedSet <int> changedLines)
        {
            // NOTE: I suggest not to touch this routine unless absolutely necessary...(things *will* break)
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (changedLines == null)
            {
                throw new ArgumentNullException(nameof(changedLines));
            }

            var iter       = changedLines.GetEnumerator();
            var lastInFile = LastInFile(file);

            Position processed = new Position(0, 0);

            while (iter.MoveNext())
            {
                QsCompilerError.Verify(0 <= iter.Current && iter.Current < file.NrLines(), "index out of range for changed line");
                if (processed.Line < iter.Current)
                {
                    var statementStart = file.PositionAfterPrevious(new Position(iter.Current, 0));
                    if (processed.IsSmallerThan(statementStart))
                    {
                        processed = statementStart;
                    }
                }

                while (processed.Line <= iter.Current && processed.IsSmallerThan(lastInFile))
                {
                    processed = processed.Copy(); // because we don't want to modify the ending of the previous code fragment ...
                    var nextEnding     = file.FragmentEnd(ref processed);
                    var extractedPiece = file.GetCodeSnippet(new LSP.Range {
                        Start = processed, End = nextEnding
                    });

                    // constructing the CodeFragment -
                    // NOTE: its Range.End is the position of the delimiting char (if such a char exists), i.e. the position right after Code ends

                    if (extractedPiece.Length > 0) // length = 0 can occur e.g. if the last piece of code in the file does not terminate with a statement ending
                    {
                        var code = file.GetLine(nextEnding.Line).ExcessBracketPositions.Contains(nextEnding.Character - 1)
                            ? extractedPiece.Substring(0, extractedPiece.Length - 1)
                            : extractedPiece;
                        if (code.Length == 0 || !CodeFragment.DelimitingChars.Contains(code.Last()))
                        {
                            code = $"{code}{CodeFragment.MissingDelimiter}";
                        }

                        var endChar   = nextEnding.Character - (extractedPiece.Length - code.Length) - 1;
                        var codeRange = new LSP.Range {
                            Start = processed, End = new Position(nextEnding.Line, endChar)
                        };
                        yield return(new CodeFragment(file.IndentationAt(codeRange.Start), codeRange, code.Substring(0, code.Length - 1), code.Last()));
                    }
                    processed = nextEnding;
                }
            }
        }
        internal static async Task <IEnumerable <CodeAction> > GetCodeActionsAsync(Document?document,
                                                                                   ICodeFixService codeFixService,
                                                                                   ICodeRefactoringService codeRefactoringService,
                                                                                   LSP.Range selection,
                                                                                   CancellationToken cancellationToken)
        {
            if (document == null)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var textSpan           = ProtocolConversions.RangeToTextSpan(selection, text);
            var codeFixCollections = await codeFixService.GetFixesAsync(document, textSpan, true, cancellationToken).ConfigureAwait(false);

            var codeRefactorings = await codeRefactoringService.GetRefactoringsAsync(document, textSpan, cancellationToken).ConfigureAwait(false);

            var codeActions = codeFixCollections.SelectMany(c => c.Fixes.Select(f => f.Action)).Concat(
                codeRefactorings.SelectMany(r => r.CodeActions.Select(ca => ca.action)));

            // Flatten out the nested codeactions.
            var nestedCodeActions = codeActions.Where(c => c is CodeAction.CodeActionWithNestedActions nc && nc.IsInlinable).SelectMany(nc => nc.NestedCodeActions);

            codeActions = codeActions.Where(c => !(c is CodeAction.CodeActionWithNestedActions)).Concat(nestedCodeActions);

            return(codeActions);
        }
Ejemplo n.º 4
0
 private static WorkspaceEdit GenerateWorkspaceEdit(
     Dictionary <string, IList <LSP.Location> > locations,
     string expectedMarkup,
     LSP.Range range)
 => new LSP.WorkspaceEdit
 {
     DocumentChanges = new TextDocumentEdit[]
     {
         new TextDocumentEdit
         {
             TextDocument = new VersionedTextDocumentIdentifier
             {
                 Uri = locations["caret"].Single().Uri
             },
             Edits = new TextEdit[]
             {
                 new TextEdit
                 {
                     NewText = expectedMarkup,
                     Range   = range
                 }
             }
         }
     }
 };
Ejemplo n.º 5
0
        private static async Task <ImmutableArray <UnifiedSuggestedActionSet>?> GetActionSetsAsync(
            Document document,
            ICodeFixService codeFixService,
            ICodeRefactoringService codeRefactoringService,
            IThreadingContext threadingContext,
            LSP.Range selection,
            CancellationToken cancellationToken)
        {
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var textSpan = ProtocolConversions.RangeToTextSpan(selection, text);

            // The logic to filter code actions requires the UI thread
            await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            var codeFixes = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread(
                document.Project.Solution.Workspace, codeFixService, document, textSpan, includeSuppressionFixes: true,
                isBlocking: false, addOperationScope: _ => null, cancellationToken);

            var codeRefactorings = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread(
                document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, isBlocking: false,
                addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken);

            var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets(codeFixes, codeRefactorings, textSpan);

            return(actionSets);
        }
Ejemplo n.º 6
0
        public async Task TestGetSemanticTokensRange_PartialDoc_RazorAsync()
        {
            // Razor docs should be returning semantic + syntactic reuslts.
            var markup =
                @"{|caret:|}// Comment
static class C { }
";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
            var range    = new LSP.Range {
                Start = new Position(1, 0), End = new Position(2, 0)
            };
            var options = ClassificationOptions.Default;
            var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync(
                document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications : true, CancellationToken.None);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                               | Modifier
                    1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                             // 'static'
                    0, 7, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                             // 'class'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C'
                    0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                        // '{'
                    0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                        // '}'
                },
            };

            await VerifyNoMultiLineTokens(testLspServer, results).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results);
        }
Ejemplo n.º 7
0
        public static async Task <ImmutableArray <CodeAction> > GetCodeActionsAsync(
            Document document,
            ICodeFixService codeFixService,
            ICodeRefactoringService codeRefactoringService,
            LSP.Range selection,
            CancellationToken cancellationToken)
        {
            var actionSets = await GetActionSetsAsync(
                document, codeFixService, codeRefactoringService, selection, cancellationToken).ConfigureAwait(false);

            if (!actionSets.HasValue)
            {
                return(ImmutableArray <CodeAction> .Empty);
            }

            var _ = ArrayBuilder <CodeAction> .GetInstance(out var codeActions);

            foreach (var set in actionSets)
            {
                foreach (var suggestedAction in set.Actions)
                {
                    // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options.
                    if (suggestedAction.OriginalCodeAction is CodeActionWithOptions)
                    {
                        continue;
                    }

                    codeActions.Add(GetNestedActionsFromActionSet(suggestedAction));
                }
            }

            return(codeActions.ToImmutable());
        }
Ejemplo n.º 8
0
 public CodeActionResolveData(string uniqueIdentifier, ImmutableArray <string> customTags, LSP.Range range, LSP.TextDocumentIdentifier textDocument)
 {
     UniqueIdentifier = uniqueIdentifier;
     CustomTags       = customTags;
     Range            = range;
     TextDocument     = textDocument;
 }
Ejemplo n.º 9
0
        public async Task LineBreakpoint()
        {
            var markup =
                @"class A
{
    void M()
    {
#if FALSE
        {|caret:|}M();
#endif
    }
}";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var caret = testLspServer.GetLocations("caret").Single();

            var expected = new LSP.Range()
            {
                Start = caret.Range.Start,
                End   = caret.Range.Start,
            };

            var result = await RunAsync(testLspServer, caret);

            AssertJsonEquals(expected, result);
        }
Ejemplo n.º 10
0
        public async Task TestGetSemanticTokensRangeAsync()
        {
            var markup =
                @"{|caret:|}// Comment
static class C { }
";

            using var testLspServer = CreateTestLspServer(markup, out var locations);

            var range = new LSP.Range {
                Start = new Position(1, 0), End = new Position(2, 0)
            };
            var results = await RunGetSemanticTokensRangeAsync(testLspServer, locations["caret"].First(), range);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                            | Modifier
                    1, 0, 6, SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                        // 'static'
                    0, 7, 5, SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                        // 'class'
                    0, 6, 1, SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C'
                    0, 2, 1, SemanticTokensCache.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                   // '{'
                    0, 2, 1, SemanticTokensCache.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                   // '}'
                },
                ResultId = "1"
            };

            await VerifyNoMultiLineTokens(testLspServer, results.Data !).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results.Data);
            Assert.Equal(expectedResults.ResultId, results.ResultId);
        }
Ejemplo n.º 11
0
        protected static int CompareRange(LSP.Range r1, LSP.Range r2)
        {
            var compareLine = r1.Start.Line.CompareTo(r2.Start.Line);
            var compareChar = r1.Start.Character.CompareTo(r2.Start.Character);

            return(compareLine != 0 ? compareLine : compareChar);
        }
Ejemplo n.º 12
0
        public async Task SimpleStatement()
        {
            var markup =
                @"class A
{
    void M()
    {
        {|caret:|}M();
    }
}";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var caret = testLspServer.GetLocations("caret").Single();

            var expected = new LSP.Range()
            {
                Start = caret.Range.Start,
                End   = new LSP.Position(caret.Range.Start.Line, caret.Range.Start.Character + "M();".Length),
            };

            var result = await RunAsync(testLspServer, caret);

            AssertJsonEquals(expected, result);
        }
Ejemplo n.º 13
0
        internal static SnapshotSpan GetSnapshotSpan(this ITextSnapshot snapshot, LSP.Range range)
        {
            Requires.NotNull(range, nameof(range));
            Requires.NotNull(snapshot, nameof(snapshot));

            return(snapshot.GetSnapshotSpan(range.Start.Line, range.Start.Character, range.End.Line, range.End.Character));
        }
Ejemplo n.º 14
0
        public async Task TestGetSemanticTokensRange_MultiLineCommentAsync()
        {
            var markup =
                @"{|caret:|}class C { /* one
two
three */ }
";
            var range = new LSP.Range {
                Start = new Position(0, 0), End = new Position(3, 0)
            };

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                               | Modifier
                    0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                    // 'class'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0,                 // 'C'
                    0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,               // '{'
                    0, 2, 6, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0,                    // '/* one'
                    1, 0, 3, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0,                    // 'two'
                    1, 0, 8, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0,                    // 'three */'
                    0, 9, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,               // '}'
                },
            };

            await VerifyNoMultiLineTokens(testLspServer, results.Data !).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results.Data);
        }
Ejemplo n.º 15
0
        public async Task TestGetSemanticTokensRange_StringLiteralAsync()
        {
            var markup =
                @"{|caret:|}class C
{
    void M()
    {
        var x = @""one
two """"
three"";
    }
}
";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var range = new LSP.Range {
                Start = new Position(0, 0), End = new Position(9, 0)
            };
            var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                                         | Modifier
                    4, 8, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0,                             // 'var'
                    1, 4, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0,               // '""'
                },
            };

            await VerifyNoMultiLineTokens(testLspServer, results.Data !).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results.Data);
        }
Ejemplo n.º 16
0
 /// <summary>
 /// Verifies the given position and range, and returns true if the given position lays within the given range.
 /// If includeEnd is true then the end point of the range is considered to be part of the range,
 /// otherwise the range is considered to include the start but excludes the end point.
 /// Throws an ArgumentNullException if the given position or range is null.
 /// Throws an ArgumentException if the given position or range is not valid.
 /// </summary>
 internal static bool IsWithinRange(this Position pos, LSP.Range range, bool includeEnd = false)
 {
     if (!IsValidPosition(pos) || !IsValidRange(range))
     {
         throw new ArgumentException("invalid position or range given for comparison");
     }
     return(range.Start.IsSmallerThanOrEqualTo(pos) && (includeEnd ? pos.IsSmallerThanOrEqualTo(range.End) : pos.IsSmallerThan(range.End)));
 }
Ejemplo n.º 17
0
 public static LSP.Location RangeToLocation(LSP.Range range, string uriString)
 {
     return(new LSP.Location()
     {
         Range = range,
         Uri = new Uri(uriString)
     });
 }
Ejemplo n.º 18
0
 /// <summary>
 /// For a given Range, returns a new Range with its starting and ending position a copy of the start and end of the given Range
 /// (i.e. does a deep copy) or null in case the given Range is null.
 /// </summary>
 public static LSP.Range Copy(this LSP.Range r)
 {
     return(r == null
         ? null
         : new LSP.Range {
         Start = r.Start.Copy(), End = r.End.Copy()
     });
 }
Ejemplo n.º 19
0
 /// <summary>
 /// Returns true if the given ranges overlap.
 /// Throws an ArgumentNullException if any of the given ranges is null.
 /// Throws an ArgumentException if any of the given ranges is not valid.
 /// </summary>
 internal static bool Overlaps(this LSP.Range range1, LSP.Range range2)
 {
     if (!IsValidRange(range1) || !IsValidRange(range2))
     {
         throw new ArgumentException("invalid range given for comparison");
     }
     var(first, second) = range1.Start.IsSmallerThan(range2.Start) ? (range1, range2) : (range2, range1);
     return(second.Start.IsSmallerThan(first.End));
 }
Ejemplo n.º 20
0
        protected static async Task <LSP.SemanticTokens> RunGetSemanticTokensRangeAsync(
            Solution solution, LSP.Location caret, LSP.Range range)
        {
            var queue = CreateRequestQueue(solution);

            return(await GetLanguageServer(solution).ExecuteRequestAsync <LSP.SemanticTokensRangeParams, LSP.SemanticTokens>(queue,
                                                                                                                             LSP.SemanticTokensMethods.TextDocumentSemanticTokensRangeName,
                                                                                                                             CreateSemanticTokensRangeParams(caret, range), new LSP.VSClientCapabilities(), null, CancellationToken.None));
        }
Ejemplo n.º 21
0
 public CodeActionsCacheItem(
     Document document,
     LSP.Range range,
     ImmutableArray <UnifiedSuggestedActionSet> cachedSuggestedActionSets)
 {
     Document = document;
     Range    = range;
     CachedSuggestedActionSets = cachedSuggestedActionSets;
 }
Ejemplo n.º 22
0
        public async Task TestGetSemanticTokensRange_StringLiteral_RazorAsync()
        {
            var markup =
                @"{|caret:|}class C
{
    void M()
    {
        var x = @""one
two """"
three"";
    }
}
";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First();
            var range    = new LSP.Range {
                Start = new Position(0, 0), End = new Position(9, 0)
            };
            var options = ClassificationOptions.Default;

            var(results, _) = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync(
                document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications : true, CancellationToken.None);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                                         | Modifier
                    0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                              // 'class'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0,                           // 'C'
                    1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '{'
                    1, 4, 4, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                              // 'void'
                    0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.MethodName], 0,                          // 'M'
                    0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '('
                    0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // ')'
                    1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '{'
                    1, 8, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0,                             // 'var'
                    0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.LocalName], 0,                           // 'x'
                    0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0,                             // '='
                    0, 2, 5, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // '@"one'
                    1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // 'two'
                    0, 4, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0,               // '""'
                    1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // 'three"'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // ';'
                    1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '}'
                    1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '}'
                },
            };

            await VerifyNoMultiLineTokens(testLspServer, results).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results);
        }
Ejemplo n.º 23
0
 private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams(
     LSP.Location caret,
     LSP.Range range
     ) =>
 new LSP.SemanticTokensRangeParams
 {
     TextDocument = new LSP.TextDocumentIdentifier {
         Uri = caret.Uri
     },
     Range = range
 };
Ejemplo n.º 24
0
 private static LSP.FoldingRange CreateFoldingRange(
     LSP.FoldingRangeKind kind,
     LSP.Range range
     ) =>
 new LSP.FoldingRange()
 {
     Kind           = kind,
     StartCharacter = range.Start.Character,
     EndCharacter   = range.End.Character,
     StartLine      = range.Start.Line,
     EndLine        = range.End.Line
 };
Ejemplo n.º 25
0
        private LSPDocumentMappingProvider GetDocumentMappingProvider(Range expectedRange, int expectedVersion, RazorLanguageKind languageKind)
        {
            var remappingResult = new RazorMapToDocumentRangesResponse()
            {
                Ranges = new[] { expectedRange }
            };
            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict);

            documentMappingProvider.Setup(d => d.MapToDocumentRangesAsync(languageKind, Uri, It.IsAny <Range[]>(), It.IsAny <CancellationToken>())).
            Returns(Task.FromResult(remappingResult));

            return(documentMappingProvider.Object);
        }
Ejemplo n.º 26
0
        public async Task TestGetSemanticTokensRange_StringLiteralAsync()
        {
            var markup =
                @"{|caret:|}class C
{
    void M()
    {
        var x = @""one
two """"
three"";
    }
}
";

            using var testLspServer = await CreateTestLspServerAsync(markup);

            var range = new LSP.Range {
                Start = new Position(0, 0), End = new Position(9, 0)
            };
            var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range);

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
                    // Line | Char | Len | Token type                                                                         | Modifier
                    0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                              // 'class'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0,                           // 'C'
                    1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '{'
                    1, 4, 4, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0,                              // 'void'
                    0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.MethodName], 0,                          // 'M'
                    0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '('
                    0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // ')'
                    1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '{'
                    1, 8, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0,                             // 'var'
                    0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.LocalName], 0,                           // 'x'
                    0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0,                             // '='
                    0, 2, 5, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // '@"one'
                    1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // 'two'
                    0, 4, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0,               // '""'
                    1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0,               // 'three"'
                    0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // ';'
                    1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '}'
                    1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0,                         // '}'
                },
            };

            await VerifyNoMultiLineTokens(testLspServer, results.Data !).ConfigureAwait(false);

            Assert.Equal(expectedResults.Data, results.Data);
        }
Ejemplo n.º 27
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);
        }
Ejemplo n.º 28
0
        /// <summary>
        /// Returns true if the given token is fully included in the given range.
        /// Throws an ArgumentNullException if token or the range delimiters are null.
        /// Throws an ArgumentException if the given range is not valid.
        /// </summary>
        internal static bool IsWithinRange(this CodeFragment token, LSP.Range range)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }
            if (!Utils.IsValidRange(range))
            {
                throw new ArgumentException("invalid range");
            }
            var tokenRange = token.GetRange();

            return(tokenRange.Start.IsWithinRange(range) && tokenRange.End.IsWithinRange(range, includeEnd: true));
        }
Ejemplo n.º 29
0
        public async Task TryResolveBreakpointRangeAsync_MappableCSharpBreakpointLocation_ReturnsHostBreakpointLocation()
        {
            // Arrange
            var hostDocumentPosition   = GetPosition(ValidBreakpointCSharp, HostTextbuffer);
            var csharpDocumentPosition = GetPosition(ValidBreakpointCSharp, CSharpTextBuffer);
            var csharpDocumentIndex    = CSharpTextBuffer.CurrentSnapshot.GetText().IndexOf(ValidBreakpointCSharp, StringComparison.Ordinal);
            var projectionProvider     = new TestLSPProjectionProvider(
                DocumentUri,
                new Dictionary <Position, ProjectionResult>()
            {
                [hostDocumentPosition] = new ProjectionResult()
                {
                    LanguageKind        = RazorLanguageKind.CSharp,
                    HostDocumentVersion = 0,
                    Position            = csharpDocumentPosition,
                    PositionIndex       = csharpDocumentIndex,
                }
            });
            var expectedCSharpBreakpointRange = new Range()
            {
                Start = csharpDocumentPosition,
                End   = new Position(csharpDocumentPosition.Line, csharpDocumentPosition.Character + ValidBreakpointCSharp.Length),
            };
            var hostBreakpointRange = new Range()
            {
                Start = hostDocumentPosition,
                End   = new Position(hostDocumentPosition.Line, hostDocumentPosition.Character + ValidBreakpointCSharp.Length),
            };
            var mappingProvider = new TestLSPDocumentMappingProvider(
                new Dictionary <Range, RazorMapToDocumentRangesResponse>()
            {
                [expectedCSharpBreakpointRange] = new RazorMapToDocumentRangesResponse()
                {
                    HostDocumentVersion = 0,
                    Ranges = new[]
                    {
                        hostBreakpointRange,
                    },
                }
            });
            var resolver = CreateResolverWith(projectionProvider: projectionProvider, documentMappingProvider: mappingProvider);

            // Act
            var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(HostTextbuffer, hostDocumentPosition.Line, hostDocumentPosition.Character, CancellationToken.None);

            // Assert
            Assert.Equal(hostBreakpointRange, breakpointRange);
        }
Ejemplo n.º 30
0
        // does not modify range
        internal static int GetRangeLength(Range range, IReadOnlyList <string> content)
        {
            Assert.IsTrue(IsValidRange(range));
            if (range.Start.Line == range.End.Line)
            {
                return(range.End.Character - range.Start.Character);
            }

            var changeLength = content[range.Start.Line].Length - range.Start.Character;

            for (var line = range.Start.Line + 1; line < range.End.Line; ++line)
            {
                changeLength += content[line].Length;
            }
            return(changeLength + range.End.Character);
        }