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)37REGISTER_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