[PATCH] D77225: [clangd] Support textDocument/semanticTokens/edits
nridge added a comment. > Tested in VSCode insiders (with a patched client to enable experimental > features). In case it's useful to others: the required change to the client to opt into this appears to be calling `registerProposedFeatures()` on the `LanguageClient` object. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D77225/new/ https://reviews.llvm.org/D77225 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D77225: [clangd] Support textDocument/semanticTokens/edits
This revision was automatically updated to reflect the committed changes. Closed by commit rG9e3063eaceec: [clangd] Support textDocument/semanticTokens/edits (authored by sammccall). Changed prior to commit: https://reviews.llvm.org/D77225?vs=254226=254536#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D77225/new/ https://reviews.llvm.org/D77225 Files: clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdLSPServer.h clang-tools-extra/clangd/Protocol.cpp clang-tools-extra/clangd/Protocol.h clang-tools-extra/clangd/SemanticHighlighting.cpp clang-tools-extra/clangd/SemanticHighlighting.h clang-tools-extra/clangd/test/initialize-params.test clang-tools-extra/clangd/test/semantic-tokens.test clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp Index: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp === --- clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -14,8 +14,10 @@ #include "TestFS.h" #include "TestTU.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include @@ -23,6 +25,9 @@ namespace clangd { namespace { +using testing::IsEmpty; +using testing::SizeIs; + MATCHER_P(LineNumber, L, "") { return arg.Line == L; } MATCHER(EmptyHighlightings, "") { return arg.Tokens.empty(); } @@ -720,25 +725,29 @@ ASSERT_EQ(Counter.Count, 1); } +// Ranges are highlighted as variables, unless highlighted as $Function etc. +std::vector tokens(llvm::StringRef MarkedText) { + Annotations A(MarkedText); + std::vector Results; + for (const Range& R : A.ranges()) +Results.push_back({HighlightingKind::Variable, R}); + for (unsigned I = 0; I < static_cast(HighlightingKind::LastKind); ++I) { +HighlightingKind Kind = static_cast(I); +for (const Range& R : A.ranges(llvm::to_string(Kind))) + Results.push_back({Kind, R}); + } + llvm::sort(Results); + return Results; +} + TEST(SemanticHighlighting, toSemanticTokens) { - auto CreatePosition = [](int Line, int Character) -> Position { -Position Pos; -Pos.line = Line; -Pos.character = Character; -return Pos; - }; + auto Results = toSemanticTokens(tokens(R"( + [[blah]] - std::vector Tokens = { - {HighlightingKind::Variable, - Range{CreatePosition(1, 1), CreatePosition(1, 5)}}, - {HighlightingKind::Function, - Range{CreatePosition(3, 4), CreatePosition(3, 7)}}, - {HighlightingKind::Variable, - Range{CreatePosition(3, 8), CreatePosition(3, 12)}}, - }; +$Function[[big]] [[bang]] + )")); - std::vector Results = toSemanticTokens(Tokens); - EXPECT_EQ(Tokens.size(), Results.size()); + ASSERT_THAT(Results, SizeIs(3)); EXPECT_EQ(Results[0].tokenType, unsigned(HighlightingKind::Variable)); EXPECT_EQ(Results[0].deltaLine, 1u); EXPECT_EQ(Results[0].deltaStart, 1u); @@ -755,6 +764,38 @@ EXPECT_EQ(Results[2].length, 4u); } +TEST(SemanticHighlighting, diffSemanticTokens) { + auto Before = toSemanticTokens(tokens(R"( +[[foo]] [[bar]] [[baz]] +[[one]] [[two]] [[three]] + )")); + EXPECT_THAT(diffTokens(Before, Before), IsEmpty()); + + auto After = toSemanticTokens(tokens(R"( +[[foo]] [[hello]] [[world]] [[baz]] +[[one]] [[two]] [[three]] + )")); + + // Replace [bar, baz] with [hello, world, baz] + auto Diff = diffTokens(Before, After); + ASSERT_THAT(Diff, SizeIs(1)); + EXPECT_EQ(1u, Diff.front().startToken); + EXPECT_EQ(2u, Diff.front().deleteTokens); + ASSERT_THAT(Diff.front().tokens, SizeIs(3)); + // hello + EXPECT_EQ(0u, Diff.front().tokens[0].deltaLine); + EXPECT_EQ(4u, Diff.front().tokens[0].deltaStart); + EXPECT_EQ(5u, Diff.front().tokens[0].length); + // world + EXPECT_EQ(0u, Diff.front().tokens[1].deltaLine); + EXPECT_EQ(6u, Diff.front().tokens[1].deltaStart); + EXPECT_EQ(5u, Diff.front().tokens[1].length); + // baz + EXPECT_EQ(0u, Diff.front().tokens[2].deltaLine); + EXPECT_EQ(6u, Diff.front().tokens[2].deltaStart); + EXPECT_EQ(3u, Diff.front().tokens[2].length); +} + TEST(SemanticHighlighting, toTheiaSemanticHighlightingInformation) { auto CreatePosition = [](int Line, int Character) -> Position { Position Pos; Index: clang-tools-extra/clangd/test/semantic-tokens.test === --- clang-tools-extra/clangd/test/semantic-tokens.test +++ clang-tools-extra/clangd/test/semantic-tokens.test @@ -6,8 +6,13 @@ "semanticTokens":{"dynamicRegistration":true} --- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.cpp","languageId":"cpp","text":"int x = 2;"}}}
[PATCH] D77225: [clangd] Support textDocument/semanticTokens/edits
hokein accepted this revision. hokein added a comment. This revision is now accepted and ready to land. this is neat, and the new semantic highlighting protocol is much nicer than the old one. Comment at: clang-tools-extra/clangd/ClangdLSPServer.cpp:1245 +static void increment(std::string ) { + for (char : llvm::reverse(S)) { +if (C != '9') { nit: add assert(C >= '0' && C <= '9'). Comment at: clang-tools-extra/clangd/SemanticHighlighting.cpp:605 +diffTokens(llvm::ArrayRef Old, + llvm::ArrayRef New) { + // For now, just replace everything from the first-last modification. nit: assert Old and New are sorted. Comment at: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp:734 +Results.push_back({HighlightingKind::Variable, R}); + for (unsigned I = 0; I < static_cast(HighlightingKind::LastKind); ++I) { +HighlightingKind Kind = static_cast(I); should be `<=`? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D77225/new/ https://reviews.llvm.org/D77225 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D77225: [clangd] Support textDocument/semanticTokens/edits
sammccall created this revision. sammccall added a reviewer: hokein. Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, mgrang, jkorous, MaskRay, ilya-biryukov. Herald added a project: clang. This returns incremental highlights as a set of edits against the previous highlights. Server-side, we compute the full set of highlights, this just saves wire-format size. For now, the diff used is trivial: everything from the first change to the last change is sent as a single edit. The wire format is grungy - the replacement offset/length refer to positions in the encoded array instead of the logical list of tokens. We use token-oriented structs and translating to LSP forms when serializing. This departs from LSP (but is consistent with semanticTokens today). Tested in VSCode insiders (with a patched client to enable experimental features). Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D77225 Files: clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdLSPServer.h clang-tools-extra/clangd/Protocol.cpp clang-tools-extra/clangd/Protocol.h clang-tools-extra/clangd/SemanticHighlighting.cpp clang-tools-extra/clangd/SemanticHighlighting.h clang-tools-extra/clangd/test/initialize-params.test clang-tools-extra/clangd/test/semantic-tokens.test clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp Index: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp === --- clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -14,8 +14,10 @@ #include "TestFS.h" #include "TestTU.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include @@ -23,6 +25,9 @@ namespace clangd { namespace { +using testing::IsEmpty; +using testing::SizeIs; + MATCHER_P(LineNumber, L, "") { return arg.Line == L; } MATCHER(EmptyHighlightings, "") { return arg.Tokens.empty(); } @@ -720,25 +725,29 @@ ASSERT_EQ(Counter.Count, 1); } +// Ranges are highlighted as variables, unless highlighted as $Function etc. +std::vector tokens(llvm::StringRef MarkedText) { + Annotations A(MarkedText); + std::vector Results; + for (const Range& R : A.ranges()) +Results.push_back({HighlightingKind::Variable, R}); + for (unsigned I = 0; I < static_cast(HighlightingKind::LastKind); ++I) { +HighlightingKind Kind = static_cast(I); +for (const Range& R : A.ranges(llvm::to_string(Kind))) + Results.push_back({Kind, R}); + } + llvm::sort(Results); + return Results; +} + TEST(SemanticHighlighting, toSemanticTokens) { - auto CreatePosition = [](int Line, int Character) -> Position { -Position Pos; -Pos.line = Line; -Pos.character = Character; -return Pos; - }; + auto Results = toSemanticTokens(tokens(R"( + [[blah]] - std::vector Tokens = { - {HighlightingKind::Variable, - Range{CreatePosition(1, 1), CreatePosition(1, 5)}}, - {HighlightingKind::Function, - Range{CreatePosition(3, 4), CreatePosition(3, 7)}}, - {HighlightingKind::Variable, - Range{CreatePosition(3, 8), CreatePosition(3, 12)}}, - }; +$Function[[big]] [[bang]] + )")); - std::vector Results = toSemanticTokens(Tokens); - EXPECT_EQ(Tokens.size(), Results.size()); + ASSERT_THAT(Results, SizeIs(3)); EXPECT_EQ(Results[0].tokenType, unsigned(HighlightingKind::Variable)); EXPECT_EQ(Results[0].deltaLine, 1u); EXPECT_EQ(Results[0].deltaStart, 1u); @@ -755,6 +764,38 @@ EXPECT_EQ(Results[2].length, 4u); } +TEST(SemanticHighlighting, diffSemanticTokens) { + auto Before = toSemanticTokens(tokens(R"( +[[foo]] [[bar]] [[baz]] +[[one]] [[two]] [[three]] + )")); + EXPECT_THAT(diffTokens(Before, Before), IsEmpty()); + + auto After = toSemanticTokens(tokens(R"( +[[foo]] [[hello]] [[world]] [[baz]] +[[one]] [[two]] [[three]] + )")); + + // Replace [bar, baz] with [hello, world, baz] + auto Diff = diffTokens(Before, After); + ASSERT_THAT(Diff, SizeIs(1)); + EXPECT_EQ(1u, Diff.front().startToken); + EXPECT_EQ(2u, Diff.front().deleteTokens); + ASSERT_THAT(Diff.front().tokens, SizeIs(3)); + // hello + EXPECT_EQ(0u, Diff.front().tokens[0].deltaLine); + EXPECT_EQ(4u, Diff.front().tokens[0].deltaStart); + EXPECT_EQ(5u, Diff.front().tokens[0].length); + // world + EXPECT_EQ(0u, Diff.front().tokens[1].deltaLine); + EXPECT_EQ(6u, Diff.front().tokens[1].deltaStart); + EXPECT_EQ(5u, Diff.front().tokens[1].length); + // baz + EXPECT_EQ(0u, Diff.front().tokens[2].deltaLine); + EXPECT_EQ(6u, Diff.front().tokens[2].deltaStart); + EXPECT_EQ(3u, Diff.front().tokens[2].length); +} + TEST(SemanticHighlighting, toTheiaSemanticHighlightingInformation) { auto CreatePosition = [](int Line, int Character) ->