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 
12 namespace clang {
13 namespace clangd {
14 namespace {
15 
16 /// Annotate all highlighting tokens in the current file. This is a hidden tweak
17 /// which is used to debug semantic highlightings.
18 /// Before:
19 ///   void f() { int abc; }
20 ///   ^^^^^^^^^^^^^^^^^^^^^
21 /// After:
22 ///   void /* entity.name.function.cpp */ f() { int /* variable.cpp */ abc; }
23 class AnnotateHighlightings : public Tweak {
24 public:
25   const char *id() const override final;
26 
prepare(const Selection & Inputs)27   bool prepare(const Selection &Inputs) override { return true; }
28   Expected<Effect> apply(const Selection &Inputs) override;
29 
title() const30   std::string title() const override { return "Annotate highlighting tokens"; }
kind() const31   llvm::StringLiteral kind() const override {
32     return CodeAction::REFACTOR_KIND;
33   }
hidden() const34   bool hidden() const override { return true; }
35 };
REGISTER_TWEAK(AnnotateHighlightings)36 REGISTER_TWEAK(AnnotateHighlightings)
37 
38 Expected<Tweak::Effect> AnnotateHighlightings::apply(const Selection &Inputs) {
39   const Decl *CommonDecl = nullptr;
40   for (auto N = Inputs.ASTSelection.commonAncestor(); N && !CommonDecl;
41        N = N->Parent)
42     CommonDecl = N->ASTNode.get<Decl>();
43 
44   std::vector<HighlightingToken> HighlightingTokens;
45   if (!CommonDecl) {
46     // Now we hit the TUDecl case where commonAncestor() returns null
47     // intendedly. We only annotate tokens in the main file, so use the default
48     // traversal scope (which is the top level decls of the main file).
49     HighlightingTokens = getSemanticHighlightings(*Inputs.AST);
50   } else {
51     // Store the existing scopes.
52     const auto &BackupScopes = Inputs.AST->getASTContext().getTraversalScope();
53     // Narrow the traversal scope to the selected node.
54     Inputs.AST->getASTContext().setTraversalScope(
55         {const_cast<Decl *>(CommonDecl)});
56     HighlightingTokens = getSemanticHighlightings(*Inputs.AST);
57     // Restore the traversal scope.
58     Inputs.AST->getASTContext().setTraversalScope(BackupScopes);
59   }
60   auto &SM = Inputs.AST->getSourceManager();
61   tooling::Replacements Result;
62   llvm::StringRef FilePath = SM.getFilename(Inputs.Cursor);
63   for (const auto &Token : HighlightingTokens) {
64     assert(Token.R.start.line == Token.R.end.line &&
65            "Token must be at the same line");
66     auto InsertOffset = positionToOffset(Inputs.Code, Token.R.start);
67     if (!InsertOffset)
68       return InsertOffset.takeError();
69 
70     auto InsertReplacement = tooling::Replacement(
71         FilePath, *InsertOffset, 0,
72         ("/* " + toTextMateScope(Token.Kind) + " */").str());
73     if (auto Err = Result.add(InsertReplacement))
74       return std::move(Err);
75   }
76   return Effect::mainFileEdit(SM, std::move(Result));
77 }
78 
79 } // namespace
80 } // namespace clangd
81 } // namespace clang
82