1 //===--- AnnotateHighlightings.cpp -------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
8 #include "SemanticHighlighting.h"
9 #include "refactor/Tweak.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/Support/ScopedPrinter.h"
17 /// Annotate all highlighting tokens in the current file. This is a hidden tweak
18 /// which is used to debug semantic highlightings.
20 /// void f() { int abc; }
21 /// ^^^^^^^^^^^^^^^^^^^^^
23 /// void /* entity.name.function.cpp */ f() { int /* variable.cpp */ abc; }
24 class AnnotateHighlightings
: public Tweak
{
26 const char *id() const final
;
28 bool prepare(const Selection
&Inputs
) override
{ return true; }
29 Expected
<Effect
> apply(const Selection
&Inputs
) override
;
31 std::string
title() const override
{ return "Annotate highlighting tokens"; }
32 llvm::StringLiteral
kind() const override
{
33 return CodeAction::REFACTOR_KIND
;
35 bool hidden() const override
{ return true; }
37 REGISTER_TWEAK(AnnotateHighlightings
)
39 Expected
<Tweak::Effect
> AnnotateHighlightings::apply(const Selection
&Inputs
) {
40 const Decl
*CommonDecl
= nullptr;
41 for (auto *N
= Inputs
.ASTSelection
.commonAncestor(); N
&& !CommonDecl
;
43 CommonDecl
= N
->ASTNode
.get
<Decl
>();
45 std::vector
<HighlightingToken
> HighlightingTokens
;
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);
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
);
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
);
71 return InsertOffset
.takeError();
73 std::string Comment
= "/* ";
74 Comment
.append(llvm::to_string(Token
.Kind
));
76 I
<= static_cast<unsigned>(HighlightingModifier::LastModifier
); ++I
) {
77 if (Token
.Modifiers
& (1 << I
)) {
79 Comment
.append(llvm::to_string(static_cast<HighlightingModifier
>(I
)));
80 Comment
.push_back(']');
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
);
89 return Effect::mainFileEdit(SM
, std::move(Result
));