diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -221,18 +221,27 @@ // the end of the Tokens). TokRef = TokRef.drop_front(Conflicting.size()); } + const auto &SM = AST.getSourceManager(); + StringRef MainCode = SM.getBuffer(SM.getMainFileID())->getBuffer(); // Add tokens indicating lines skipped by the preprocessor. for (const Range &R : AST.getMacros().SkippedRanges) { // Create one token for each line in the skipped range, so it works // with line-based diffing. assert(R.start.line <= R.end.line); for (int Line = R.start.line; Line <= R.end.line; ++Line) { - // Don't bother computing the offset for the end of the line, just use - // zero. The client will treat this highlighting kind specially, and - // highlight the entire line visually (i.e. not just to where the text - // on the line ends, but to the end of the screen). - NonConflicting.push_back({HighlightingKind::InactiveCode, - {Position{Line, 0}, Position{Line, 0}}}); + auto StartOfLine = positionToOffset(MainCode, Position{Line, 0}); + if (!StartOfLine) { + elog("Failed to convert position to offset: {0}", + StartOfLine.takeError()); + continue; + } + size_t LineLength = MainCode.substr(*StartOfLine).find('\n'); + size_t EndOfLine = LineLength == llvm::StringRef::npos + ? MainCode.size() - 1 // EOF + : *StartOfLine + LineLength; + NonConflicting.push_back( + {HighlightingKind::InactiveCode, + {Position{Line, 0}, offsetToPosition(MainCode, EndOfLine)}}); } } // Re-sort the tokens because that's what the diffing expects. @@ -493,9 +502,6 @@ std::vector Result; const HighlightingToken *Last = nullptr; for (const HighlightingToken &Tok : Tokens) { - // FIXME: support inactive code - we need to provide the actual bounds. - if (Tok.Kind == HighlightingKind::InactiveCode) - continue; Result.emplace_back(); SemanticToken &Out = Result.back(); // deltaStart/deltaLine are relative if possible. diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -79,7 +79,8 @@ /// $Primitive[[int]] $Field[[a]] = 0; /// }; std::string annotate(llvm::StringRef Input, - llvm::ArrayRef Tokens) { + llvm::ArrayRef Tokens, + size_t NextChar = 0) { assert(std::is_sorted( Tokens.begin(), Tokens.end(), [](const HighlightingToken &L, const HighlightingToken &R) { @@ -87,18 +88,36 @@ })); std::string Result; - unsigned NextChar = 0; - for (auto &T : Tokens) { - unsigned StartOffset = llvm::cantFail(positionToOffset(Input, T.R.start)); - unsigned EndOffset = llvm::cantFail(positionToOffset(Input, T.R.end)); + for (size_t I = 0; I < Tokens.size(); ++I) { + const auto &Current = Tokens[I]; + unsigned StartOffset = + llvm::cantFail(positionToOffset(Input, Current.R.start)); + assert(NextChar <= StartOffset && "Highlighting tokens must not overlap!"); + unsigned EndOffset = llvm::cantFail(positionToOffset(Input, Current.R.end)); assert(StartOffset <= EndOffset); - assert(NextChar <= StartOffset); - + const auto *End = + llvm::find_if(llvm::makeArrayRef(Tokens.begin() + I + 1, Tokens.end()), + [&](const HighlightingToken &Next) { + return Current.R.end <= Next.R.start; + }); + // Tokens whose ranges are contained by the Current range, we recursively + // annotate them. + auto ContainedTokens = llvm::makeArrayRef(Tokens.begin() + I + 1, End); Result += Input.substr(NextChar, StartOffset - NextChar); - Result += std::string( - llvm::formatv("${0}[[{1}]]", T.Kind, - Input.substr(StartOffset, EndOffset - StartOffset))); + if (ContainedTokens.empty()) { + // No extra tokens contained by Current. + Result += + llvm::formatv("${0}[[{1}]]", Current.Kind, + Input.substr(StartOffset, EndOffset - StartOffset)); + NextChar = EndOffset; + continue; + } + + auto Annotated = + annotate(Input.substr(0, EndOffset), ContainedTokens, StartOffset); + Result += llvm::formatv("${0}[[{1}]]", Current.Kind, Annotated); NextChar = EndOffset; + I += ContainedTokens.size(); } Result += Input.substr(NextChar); return Result; @@ -503,11 +522,11 @@ #define $Macro[[test]] #undef $Macro[[test]] -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef $Macro[[test]]]] +$InactiveCode[[#endif]] -$InactiveCode[[]] #if defined($Macro[[test]]) -$InactiveCode[[]] #endif +$InactiveCode[[#if defined($Macro[[test]])]] +$InactiveCode[[#endif]] )cpp", R"cpp( struct $Class[[S]] { @@ -614,8 +633,8 @@ R"cpp( // Code in the preamble. // Inactive lines get an empty InactiveCode token at the beginning. -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef $Macro[[test]]]] +$InactiveCode[[#endif]] // A declaration to cause the preamble to end. int $Variable[[EndPreamble]]; @@ -623,17 +642,17 @@ // Code after the preamble. // Code inside inactive blocks does not get regular highlightings // because it's not part of the AST. -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] int Inactive2; -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef $Macro[[test]]]] +$InactiveCode[[int Inactive2;]] +$InactiveCode[[#endif]] #ifndef $Macro[[test]] int $Variable[[Active1]]; #endif -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] int Inactive3; -$InactiveCode[[]] #else +$InactiveCode[[#ifdef $Macro[[test]]]] +$InactiveCode[[int Inactive3;]] +$InactiveCode[[#else]] int $Variable[[Active2]]; #endif )cpp",