// 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 } }
/// <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); }
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 } } } } };
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); }
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); }
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()); }
public CodeActionResolveData(string uniqueIdentifier, ImmutableArray <string> customTags, LSP.Range range, LSP.TextDocumentIdentifier textDocument) { UniqueIdentifier = uniqueIdentifier; CustomTags = customTags; Range = range; TextDocument = textDocument; }
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); }
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); }
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); }
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); }
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)); }
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); }
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); }
/// <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))); }
public static LSP.Location RangeToLocation(LSP.Range range, string uriString) { return(new LSP.Location() { Range = range, Uri = new Uri(uriString) }); }
/// <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() }); }
/// <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)); }
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)); }
public CodeActionsCacheItem( Document document, LSP.Range range, ImmutableArray <UnifiedSuggestedActionSet> cachedSuggestedActionSets) { Document = document; Range = range; CachedSuggestedActionSets = cachedSuggestedActionSets; }
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); }
private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams( LSP.Location caret, LSP.Range range ) => new LSP.SemanticTokensRangeParams { TextDocument = new LSP.TextDocumentIdentifier { Uri = caret.Uri }, Range = range };
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 };
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); }
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); }
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); }
/// <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)); }
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); }
// 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); }