Skip to content

Commit

Permalink
[clangd] Added functionality for getting semantic highlights for vari…
Browse files Browse the repository at this point in the history
…able and function declarations

llvm-svn: 364421
  • Loading branch information
jvikstrom committed Jun 26, 2019
1 parent 1388914 commit 09bc665
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_clang_library(clangDaemon
Quality.cpp
RIFF.cpp
Selection.cpp
SemanticHighlighting.cpp
SourceCode.cpp
QueryDriverDatabase.cpp
Threading.cpp
Expand Down
78 changes: 78 additions & 0 deletions clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===--- SemanticHighlighting.cpp - -------------------------- ---*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "SemanticHighlighting.h"
#include "Logger.h"
#include "SourceCode.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"

namespace clang {
namespace clangd {
namespace {

// Collects all semantic tokens in an ASTContext.
class HighlightingTokenCollector
: public RecursiveASTVisitor<HighlightingTokenCollector> {
std::vector<HighlightingToken> Tokens;
ASTContext &Ctx;
const SourceManager &SM;

public:
HighlightingTokenCollector(ParsedAST &AST)
: Ctx(AST.getASTContext()), SM(AST.getSourceManager()) {}

std::vector<HighlightingToken> collectTokens() {
Tokens.clear();
TraverseAST(Ctx);
return Tokens;
}

bool VisitVarDecl(VarDecl *Var) {
addToken(Var, HighlightingKind::Variable);
return true;
}
bool VisitFunctionDecl(FunctionDecl *Func) {
addToken(Func, HighlightingKind::Function);
return true;
}

private:
void addToken(const NamedDecl *D, HighlightingKind Kind) {
if (D->getLocation().isMacroID())
// FIXME: skip tokens inside macros for now.
return;

if (D->getDeclName().isEmpty())
// Don't add symbols that don't have any length.
return;

auto R = getTokenRange(SM, Ctx.getLangOpts(), D->getLocation());
if (!R) {
// R should always have a value, if it doesn't something is very wrong.
elog("Tried to add semantic token with an invalid range");
return;
}

Tokens.push_back({Kind, R.getValue()});
}
};

} // namespace

bool operator==(const HighlightingToken &Lhs, const HighlightingToken &Rhs) {
return Lhs.Kind == Rhs.Kind && Lhs.R == Rhs.R;
}

std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST) {
AST.getASTContext().setTraversalScope(AST.getLocalTopLevelDecls());
return HighlightingTokenCollector(AST).collectTokens();
}

} // namespace clangd
} // namespace clang
37 changes: 37 additions & 0 deletions clang-tools-extra/clangd/SemanticHighlighting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//==-- SemanticHighlighting.h - Generating highlights from the AST-- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICHIGHLIGHT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICHIGHLIGHT_H

#include "ClangdUnit.h"

namespace clang {
namespace clangd {

enum class HighlightingKind {
Variable,
Function,
};

// Contains all information needed for the highlighting a token.
struct HighlightingToken {
HighlightingKind Kind;
Range R;
};

bool operator==(const HighlightingToken &Lhs, const HighlightingToken &Rhs);

// Returns all HighlightingTokens from an AST. Only generates highlights for the
// main AST.
std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST);

} // namespace clangd
} // namespace clang

#endif
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ add_unittest(ClangdUnitTests ClangdTests
RenameTests.cpp
RIFFTests.cpp
SelectionTests.cpp
SemanticHighlightingTests.cpp
SerializationTests.cpp
SourceCodeTests.cpp
SymbolCollectorTests.cpp
Expand Down
69 changes: 69 additions & 0 deletions clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//==- SemanticHighlightingTests.cpp - SemanticHighlighting tests-*- C++ -* -==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "Annotations.h"
#include "SemanticHighlighting.h"
#include "TestTU.h"
#include "gmock/gmock.h"

namespace clang {
namespace clangd {
namespace {

std::vector<HighlightingToken>
makeHighlightingTokens(llvm::ArrayRef<Range> Ranges, HighlightingKind Kind) {
std::vector<HighlightingToken> Tokens(Ranges.size());
for (int I = 0, End = Ranges.size(); I < End; ++I) {
Tokens[I].R = Ranges[I];
Tokens[I].Kind = Kind;
}

return Tokens;
}

void checkHighlightings(llvm::StringRef Code) {
Annotations Test(Code);
auto AST = TestTU::withCode(Test.code()).build();
static const std::map<HighlightingKind, std::string> KindToString{
{HighlightingKind::Variable, "Variable"},
{HighlightingKind::Function, "Function"}};
std::vector<HighlightingToken> ExpectedTokens;
for (const auto &KindString : KindToString) {
std::vector<HighlightingToken> Toks =
makeHighlightingTokens(Test.ranges(KindString.second), KindString.first);
ExpectedTokens.insert(ExpectedTokens.end(), Toks.begin(), Toks.end());
}

auto ActualTokens = getSemanticHighlightings(AST);
EXPECT_THAT(ActualTokens, testing::UnorderedElementsAreArray(ExpectedTokens));
}

TEST(SemanticHighlighting, GetsCorrectTokens) {
const char *TestCases[] = {
R"cpp(
struct A {
double SomeMember;
};
struct {
} $Variable[[HStruct]];
void $Function[[foo]](int $Variable[[a]]) {
auto $Variable[[VeryLongVariableName]] = 12312;
A $Variable[[aa]];
}
)cpp",
R"cpp(
void $Function[[foo]](int);
)cpp"};
for (const auto &TestCase : TestCases) {
checkHighlightings(TestCase);
}
}

} // namespace
} // namespace clangd
} // namespace clang

0 comments on commit 09bc665

Please sign in to comment.