96 lines · cpp
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-exception6//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 tweak18/// 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 null48 // intendedly. We only annotate tokens in the main file, so use the default49 // 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 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 *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(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(Inputs.Code, Token.R.start);71 if (!InsertOffset)72 return InsertOffset.takeError();73 74 std::string Comment = "/* ";75 Comment.append(llvm::to_string(Token.Kind));76 for (unsigned I = 0;77 I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) {78 if (Token.Modifiers & (1 << I)) {79 Comment.append(" [");80 Comment.append(llvm::to_string(static_cast<HighlightingModifier>(I)));81 Comment.push_back(']');82 }83 }84 Comment.append(" */");85 auto InsertReplacement =86 tooling::Replacement(FilePath, *InsertOffset, 0, Comment);87 if (auto Err = Result.add(InsertReplacement))88 return std::move(Err);89 }90 return Effect::mainFileEdit(SM, std::move(Result));91}92 93} // namespace94} // namespace clangd95} // namespace clang96