xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp (revision 3f6a904b2f3d8e974b223097956bb1ea51822782)
1 //===--- AnnotateHighlightings.cpp -------------------------------*- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "SemanticHighlighting.h"
9 #include "refactor/Tweak.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/Support/ScopedPrinter.h"
12 
13 namespace clang {
14 namespace clangd {
15 namespace {
16 
17 /// Annotate all highlighting tokens in the current file. This is a hidden tweak
18 /// which is used to debug semantic highlightings.
19 /// Before:
20 ///   void f() { int abc; }
21 ///   ^^^^^^^^^^^^^^^^^^^^^
22 /// After:
23 ///   void /* entity.name.function.cpp */ f() { int /* variable.cpp */ abc; }
24 class AnnotateHighlightings : public Tweak {
25 public:
26   const char *id() const final;
27 
prepare(const Selection & Inputs)28   bool prepare(const Selection &Inputs) override { return true; }
29   Expected<Effect> apply(const Selection &Inputs) override;
30 
title() const31   std::string title() const override { return "Annotate highlighting tokens"; }
kind() const32   llvm::StringLiteral kind() const override {
33     return CodeAction::REFACTOR_KIND;
34   }
hidden() const35   bool hidden() const override { return true; }
36 };
REGISTER_TWEAK(AnnotateHighlightings)37 REGISTER_TWEAK(AnnotateHighlightings)
38 
39 Expected<Tweak::Effect> AnnotateHighlightings::apply(const Selection &Inputs) {
40   const Decl *CommonDecl = nullptr;
41   for (auto *N = Inputs.ASTSelection.commonAncestor(); N && !CommonDecl;
42        N = N->Parent)
43     CommonDecl = N->ASTNode.get<Decl>();
44 
45   std::vector<HighlightingToken> HighlightingTokens;
46   if (!CommonDecl) {
47     // Now we hit the TUDecl case where commonAncestor() returns null
48     // intendedly. We only annotate tokens in the main file, so use the default
49     // traversal scope (which is the top level decls of the main file).
50     HighlightingTokens = getSemanticHighlightings(
51         *Inputs.AST, /*IncludeInactiveRegionTokens=*/true);
52   } else {
53     // Store the existing scopes.
54     const auto &BackupScopes = Inputs.AST->getASTContext().getTraversalScope();
55     // Narrow the traversal scope to the selected node.
56     Inputs.AST->getASTContext().setTraversalScope(
57         {const_cast<Decl *>(CommonDecl)});
58     HighlightingTokens = getSemanticHighlightings(
59         *Inputs.AST, /*IncludeInactiveRegionTokens=*/true);
60     // Restore the traversal scope.
61     Inputs.AST->getASTContext().setTraversalScope(BackupScopes);
62   }
63   auto &SM = Inputs.AST->getSourceManager();
64   tooling::Replacements Result;
65   llvm::StringRef FilePath = SM.getFilename(Inputs.Cursor);
66   for (const auto &Token : HighlightingTokens) {
67     assert(Token.R.start.line == Token.R.end.line &&
68            "Token must be at the same line");
69     auto InsertOffset = positionToOffset(Inputs.Code, Token.R.start);
70     if (!InsertOffset)
71       return InsertOffset.takeError();
72 
73     std::string Comment = "/* ";
74     Comment.append(llvm::to_string(Token.Kind));
75     for (unsigned I = 0;
76          I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) {
77       if (Token.Modifiers & (1 << I)) {
78         Comment.append(" [");
79         Comment.append(llvm::to_string(static_cast<HighlightingModifier>(I)));
80         Comment.push_back(']');
81       }
82     }
83     Comment.append(" */");
84     auto InsertReplacement =
85         tooling::Replacement(FilePath, *InsertOffset, 0, Comment);
86     if (auto Err = Result.add(InsertReplacement))
87       return std::move(Err);
88   }
89   return Effect::mainFileEdit(SM, std::move(Result));
90 }
91 
92 } // namespace
93 } // namespace clangd
94 } // namespace clang
95