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