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
13namespace clang {
14namespace clangd {
15namespace {
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; }
24class AnnotateHighlightings : public Tweak {
25public:
26 const char *id() const final;
27
28 bool prepare(const Selection &Inputs) override { return true; }
29 Expected<Effect> apply(const Selection &Inputs) override;
30
31 std::string title() const override { return "Annotate highlighting tokens"; }
32 llvm::StringLiteral kind() const override {
33 return CodeAction::REFACTOR_KIND;
34 }
35 bool hidden() const override { return true; }
36};
37REGISTER_TWEAK(AnnotateHighlightings)
38
39Expected<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 AST&: *Inputs.AST, /*IncludeInactiveRegionTokens=*/true);
52 } else {
53 // Store the existing scopes.
54 const std::vector<Decl *> BackupScopes =
55 Inputs.AST->getASTContext().getTraversalScope();
56 // Narrow the traversal scope to the selected node.
57 Inputs.AST->getASTContext().setTraversalScope(
58 {const_cast<Decl *>(CommonDecl)});
59 HighlightingTokens = getSemanticHighlightings(
60 AST&: *Inputs.AST, /*IncludeInactiveRegionTokens=*/true);
61 // Restore the traversal scope.
62 Inputs.AST->getASTContext().setTraversalScope(BackupScopes);
63 }
64 auto &SM = Inputs.AST->getSourceManager();
65 tooling::Replacements Result;
66 llvm::StringRef FilePath = SM.getFilename(SpellingLoc: Inputs.Cursor);
67 for (const auto &Token : HighlightingTokens) {
68 assert(Token.R.start.line == Token.R.end.line &&
69 "Token must be at the same line");
70 auto InsertOffset = positionToOffset(Code: Inputs.Code, P: Token.R.start);
71 if (!InsertOffset)
72 return InsertOffset.takeError();
73
74 std::string Comment = "/* ";
75 Comment.append(str: llvm::to_string(Value: Token.Kind));
76 for (unsigned I = 0;
77 I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) {
78 if (Token.Modifiers & (1 << I)) {
79 Comment.append(s: " [");
80 Comment.append(str: llvm::to_string(Value: static_cast<HighlightingModifier>(I)));
81 Comment.push_back(c: ']');
82 }
83 }
84 Comment.append(s: " */");
85 auto InsertReplacement =
86 tooling::Replacement(FilePath, *InsertOffset, 0, Comment);
87 if (auto Err = Result.add(R: InsertReplacement))
88 return std::move(Err);
89 }
90 return Effect::mainFileEdit(SM, Replacements: std::move(Result));
91}
92
93} // namespace
94} // namespace clangd
95} // namespace clang
96

source code of clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp