SemanticTokensTests.cs 8.0 KB
Newer Older
A
Allison Chou 已提交
1 2 3 4 5 6 7 8
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System.Linq;
using System.Threading.Tasks;
A
Allison Chou 已提交
9
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
A
Allison Chou 已提交
10
using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens;
A
Allison Chou 已提交
11
using Microsoft.CodeAnalysis.Text;
A
Allison Chou 已提交
12 13 14 15 16 17
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Xunit;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens
{
A
Allison Chou 已提交
18
    public class SemanticTokensTests : AbstractSemanticTokensTests
A
Allison Chou 已提交
19 20 21 22 23 24 25
    {
        [Fact]
        public async Task TestGetSemanticTokensAsync()
        {
            var markup =
@"{|caret:|}// Comment
static class C { }";
A
Allison Chou 已提交
26

A
Allison Chou 已提交
27
            using var workspace = CreateTestWorkspace(markup, out var locations);
28
            var cache = GetSemanticTokensCache(workspace);
A
Allison Chou 已提交
29 30 31 32 33 34
            var results = await RunGetSemanticTokensAsync(workspace.CurrentSolution, locations["caret"].First());

            var expectedResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
A
Allison Chou 已提交
35
                    // Line | Char | Len | Token type                                                            | Modifier
A
Allison Chou 已提交
36 37 38 39 40 41
                       0,     0,     10,   SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment],  0, // '// Comment'
                       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[LSP.SemanticTokenTypes.Operator], 0, // '{'
                       0,     2,     1,    SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '}'
A
Allison Chou 已提交
42
                },
43
                ResultId = "1"
A
Allison Chou 已提交
44 45
            };

A
Allison Chou 已提交
46 47
            Assert.Equal(expectedResults.Data, results.Data);
            Assert.Equal(expectedResults.ResultId, results.ResultId);
A
Allison Chou 已提交
48 49
        }

A
Allison Chou 已提交
50 51 52 53 54 55 56 57 58 59 60 61
        /// <summary>
        /// Tests all three handlers in succession and makes sure we receive the expected result at each stage.
        /// </summary>
        [Fact]
        public async Task TestAllHandlersAsync()
        {
            var markup =
@"{|caret:|}// Comment
static class C { }
";

            using var workspace = CreateTestWorkspace(markup, out var locations);
62
            var cache = GetSemanticTokensCache(workspace);
A
Allison Chou 已提交
63
            var caretLocation = locations["caret"].First();
A
Allison Chou 已提交
64

A
Allison Chou 已提交
65 66 67 68
            // 1. Range handler
            var range = new LSP.Range { Start = new Position(1, 0), End = new Position(2, 0) };
            var rangeResults = await RunGetSemanticTokensRangeAsync(workspace.CurrentSolution, caretLocation, range);
            var expectedRangeResults = new LSP.SemanticTokens
A
Allison Chou 已提交
69
            {
A
Allison Chou 已提交
70 71
                Data = new int[]
                {
A
Allison Chou 已提交
72
                    // Line | Char | Len | Token type                                                            | Modifier
A
Allison Chou 已提交
73 74 75 76 77
                       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[LSP.SemanticTokenTypes.Operator], 0, // '{'
                       0,     2,     1,    SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '}'
A
Allison Chou 已提交
78
                },
79
                ResultId = "1"
A
Allison Chou 已提交
80
            };
A
Allison Chou 已提交
81 82 83 84 85 86 87 88 89 90

            Assert.Equal(expectedRangeResults.Data, rangeResults.Data);
            Assert.Equal(expectedRangeResults.ResultId, rangeResults.ResultId);

            // 2. Whole document handler
            var wholeDocResults = await RunGetSemanticTokensAsync(workspace.CurrentSolution, caretLocation);
            var expectedWholeDocResults = new LSP.SemanticTokens
            {
                Data = new int[]
                {
A
Allison Chou 已提交
91
                    // Line | Char | Len | Token type                                                            | Modifier
A
Allison Chou 已提交
92 93 94 95 96 97
                       0,     0,     10,   SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment],  0, // '// Comment'
                       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[LSP.SemanticTokenTypes.Operator], 0, // '{'
                       0,     2,     1,    SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '}'
A
Allison Chou 已提交
98
                },
99
                ResultId = "2"
A
Allison Chou 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
            };

            Assert.Equal(expectedWholeDocResults.Data, wholeDocResults.Data);
            Assert.Equal(expectedWholeDocResults.ResultId, wholeDocResults.ResultId);

            // 3. Edits handler - insert newline at beginning of file
            // Markup becomes:
            //
            //   // Comment
            //   static class C { }";
            var currentDocText = await workspace.CurrentSolution.Projects.First().Documents.First().GetTextAsync();
            var changedSourceText = currentDocText.WithChanges(new TextChange(new TextSpan(0, 0), "\n"));
            var docId = ((TestWorkspace)workspace).Documents.First().Id;
            ((TestWorkspace)workspace).ChangeDocument(docId, changedSourceText);
            UpdateSolutionProvider((TestWorkspace)workspace, workspace.CurrentSolution);

116
            var editResults = await RunGetSemanticTokensEditsAsync(workspace.CurrentSolution, caretLocation, previousResultId: "2");
A
Allison Chou 已提交
117 118 119 120

            var expectedEdit = SemanticTokensEditsHandler.GenerateEdit(0, 1, new int[] { 1 });

            Assert.Equal(expectedEdit, ((LSP.SemanticTokensEdits)editResults).Edits.First());
121
            Assert.Equal("3", ((LSP.SemanticTokensEdits)editResults).ResultId);
A
Allison Chou 已提交
122 123 124 125 126 127 128

            // 4. Re-request whole document handler (may happen if LSP runs into an error)
            var wholeDocResults2 = await RunGetSemanticTokensAsync(workspace.CurrentSolution, caretLocation);
            var expectedWholeDocResults2 = new LSP.SemanticTokens
            {
                Data = new int[]
                {
A
Allison Chou 已提交
129
                    // Line | Char | Len | Token type                                                            | Modifier
A
Allison Chou 已提交
130 131 132 133 134 135
                       1,     0,     10,   SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment],  0, // '// Comment'
                       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[LSP.SemanticTokenTypes.Operator], 0, // '{'
                       0,     2,     1,    SemanticTokensCache.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '}'
A
Allison Chou 已提交
136
                },
137
                ResultId = "4"
A
Allison Chou 已提交
138 139 140 141 142
            };

            Assert.Equal(expectedWholeDocResults2.Data, wholeDocResults2.Data);
            Assert.Equal(expectedWholeDocResults2.ResultId, wholeDocResults2.ResultId);
        }
A
Allison Chou 已提交
143 144
    }
}