diff --git a/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp @@ -0,0 +1,83 @@ +//===--- AnnotateHighlightings.cpp -------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "SemanticHighlighting.h" +#include "refactor/Tweak.h" +#include "llvm/ADT/ScopeExit.h" + +namespace clang { +namespace clangd { +namespace { + +// FIXME: move it to SemanticHighlighting.h. +llvm::StringRef toTextMateScope(HighlightingKind Kind) { + static const auto &TextMateLookupTable = getTextMateScopeLookupTable(); + auto LookupIndex = static_cast(Kind); + assert(LookupIndex < TextMateLookupTable.size() && + !TextMateLookupTable[LookupIndex].empty()); + return TextMateLookupTable[LookupIndex].front(); +} + +/// Annotate all highlighting tokens in the current file. This is a hidden tweak +/// which is used to debug semantic highlightings. +/// Before: +/// void f() { int abc; } +/// ^^^^^^^^^^^^^^^^^^^^^ +/// After: +/// void /* entity.name.function.cpp */ f() { int /* variable.cpp */ abc; } +class AnnotateHighlightings : public Tweak { +public: + const char *id() const override final; + + bool prepare(const Selection &Inputs) override { + for (auto N = Inputs.ASTSelection.commonAncestor(); N && !InterestedDecl; + N = N->Parent) + InterestedDecl = N->ASTNode.get(); + return InterestedDecl; + } + Expected apply(const Selection &Inputs) override; + + std::string title() const override { return "Annotate highlighting tokens"; } + Intent intent() const override { return Refactor; } + bool hidden() const override { return true; } + +private: + const Decl *InterestedDecl = nullptr; +}; +REGISTER_TWEAK(AnnotateHighlightings) + +Expected AnnotateHighlightings::apply(const Selection &Inputs) { + // Store the existing scopes. + const auto &BackupScopes = Inputs.AST.getASTContext().getTraversalScope(); + // Narrow the traversal scope to the selected node. + Inputs.AST.getASTContext().setTraversalScope( + {const_cast(InterestedDecl)}); + auto HighlightingTokens = getSemanticHighlightings(Inputs.AST); + // Restore the traversal scope. + Inputs.AST.getASTContext().setTraversalScope(BackupScopes); + + auto &SM = Inputs.AST.getSourceManager(); + tooling::Replacements Result; + for (const auto &Token : HighlightingTokens) { + assert(Token.R.start.line == Token.R.end.line && + "Token must be at the same line"); + auto InsertOffset = positionToOffset(Inputs.Code, Token.R.start); + if (!InsertOffset) + return InsertOffset.takeError(); + + auto InsertReplacement = tooling::Replacement( + SM.getFileEntryForID(SM.getMainFileID())->getName(), *InsertOffset, 0, + ("/* " + toTextMateScope(Token.Kind) + " */").str()); + if (auto Err = Result.add(InsertReplacement)) + return std::move(Err); + } + return Effect::applyEdit(Result); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -12,6 +12,7 @@ # $ to a list of sources, see # clangd/tool/CMakeLists.txt for an example. add_clang_library(clangDaemonTweaks OBJECT + AnnotateHighlightings.cpp DumpAST.cpp RawStringLiteral.cpp SwapIfBranches.cpp diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -278,6 +278,14 @@ EXPECT_THAT(getMessage(ID, Input), ::testing::HasSubstr("0 | int x")); } +TEST(TweakTest, AnnotateHighlightings) { + llvm::StringLiteral ID = "AnnotateHighlightings"; + checkAvailable(ID, "^vo^id^ ^f(^) {^}^"); // available everywhere. + const char *Input = "void ^f() {}"; + const char *Output = "void /* entity.name.function.cpp */f() {}"; + checkTransform(ID, Input, Output); +} + } // namespace } // namespace clangd } // namespace clang