diff --git a/clang-tools-extra/clangd/ParsedAST.h b/clang-tools-extra/clangd/ParsedAST.h --- a/clang-tools-extra/clangd/ParsedAST.h +++ b/clang-tools-extra/clangd/ParsedAST.h @@ -97,13 +97,18 @@ /// (!) does not have tokens from the preamble. const syntax::TokenBuffer &getTokens() const { return Tokens; } + const std::vector &getSkippedRanges() const { + return SkippedRanges; + } + private: ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, syntax::TokenBuffer Tokens, MainFileMacros Macros, std::vector LocalTopLevelDecls, std::vector Diags, IncludeStructure Includes, - CanonicalIncludes CanonIncludes); + CanonicalIncludes CanonIncludes, + std::vector SkippedRanges); // In-memory preambles must outlive the AST, it is important that this member // goes before Clang and Action. @@ -130,6 +135,8 @@ std::vector LocalTopLevelDecls; IncludeStructure Includes; CanonicalIncludes CanonIncludes; + // Ranges skipped during preprocessing. + std::vector SkippedRanges; }; /// Build an AST from provided user inputs. This function does not check if diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -208,6 +208,19 @@ const LangOptions &LangOpts; }; +class CollectSkippedRanges : public PPCallbacks { +public: + explicit CollectSkippedRanges(std::vector &SkippedRanges) + : SkippedRanges(SkippedRanges) {} + + void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override { + SkippedRanges.push_back(Range); + } + +private: + std::vector &SkippedRanges; +}; + } // namespace void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) { @@ -348,6 +361,9 @@ Clang->getPreprocessor().addPPCallbacks( std::make_unique(Clang->getSourceManager(), Clang->getLangOpts(), Macros)); + std::vector SkippedRanges; + Clang->getPreprocessor().addPPCallbacks( + std::make_unique(SkippedRanges)); // Copy over the includes from the preamble, then combine with the // non-preamble includes below. @@ -403,7 +419,7 @@ return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), std::move(Tokens), std::move(Macros), std::move(ParsedDecls), std::move(Diags), std::move(Includes), - std::move(CanonIncludes)); + std::move(CanonIncludes), std::move(SkippedRanges)); } ParsedAST::ParsedAST(ParsedAST &&Other) = default; @@ -491,12 +507,14 @@ syntax::TokenBuffer Tokens, MainFileMacros Macros, std::vector LocalTopLevelDecls, std::vector Diags, IncludeStructure Includes, - CanonicalIncludes CanonIncludes) + CanonicalIncludes CanonIncludes, + std::vector SkippedRanges) : Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Tokens(std::move(Tokens)), Macros(std::move(Macros)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), - Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) { + Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)), + SkippedRanges(std::move(SkippedRanges)) { assert(this->Clang); assert(this->Action); } diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1209,6 +1209,8 @@ int Line = 0; /// The base64 encoded string of highlighting tokens. std::string Tokens; + /// Is the line in an inactive preprocessor branch? + bool IsInactive = false; }; bool operator==(const SemanticHighlightingInformation &Lhs, const SemanticHighlightingInformation &Rhs); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1063,7 +1063,8 @@ llvm::json::Value toJSON(const SemanticHighlightingInformation &Highlighting) { return llvm::json::Object{{"line", Highlighting.Line}, - {"tokens", Highlighting.Tokens}}; + {"tokens", Highlighting.Tokens}, + {"isInactive", Highlighting.IsInactive}}; } llvm::json::Value toJSON(const SemanticHighlightingParams &Highlighting) { diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h --- a/clang-tools-extra/clangd/SemanticHighlighting.h +++ b/clang-tools-extra/clangd/SemanticHighlighting.h @@ -41,8 +41,9 @@ TemplateParameter, Primitive, Macro, + InactiveCode, - LastKind = Macro + LastKind = InactiveCode }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K); @@ -59,6 +60,7 @@ struct LineHighlightings { int Line; std::vector Tokens; + bool IsInactive; }; bool operator==(const LineHighlightings &L, const LineHighlightings &R); 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 @@ -137,6 +137,27 @@ // the end of the Tokens). TokRef = TokRef.drop_front(Conflicting.size()); } + // Add inactive highlighting tokens. + const SourceManager &SM = AST.getSourceManager(); + for (const SourceRange &R : AST.getSkippedRanges()) { + if (isInsideMainFile(R.getBegin(), SM)) { + // Create one token for each line in the skipped range, so it works + // with line-based diffing. + Position Begin = sourceLocToPosition(SM, R.getBegin()); + Position End = sourceLocToPosition(SM, R.getEnd()); + assert(Begin.line <= End.line); + for (int Line = Begin.line; Line < 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}}}); + } + } + } + // Re-sort the tokens because that's what the diffing expects. + llvm::sort(NonConflicting); return NonConflicting; } @@ -347,6 +368,8 @@ return OS << "Primitive"; case HighlightingKind::Macro: return OS << "Macro"; + case HighlightingKind::InactiveCode: + return OS << "InactiveCode"; } llvm_unreachable("invalid HighlightingKind"); } @@ -391,8 +414,21 @@ LineNumber = NextLineNumber()) { NewLine = takeLine(New, NewLine.end(), LineNumber); OldLine = takeLine(Old, OldLine.end(), LineNumber); - if (NewLine != OldLine) - DiffedLines.push_back({LineNumber, NewLine}); + if (NewLine != OldLine) { + DiffedLines.push_back({LineNumber, NewLine, /*IsInactive=*/false}); + + // Turn a HighlightingKind::InactiveCode token into the IsInactive flag. + auto &AddedLine = DiffedLines.back(); + for (auto Iter = AddedLine.Tokens.begin(); + Iter != AddedLine.Tokens.end();) { + if (Iter->Kind == HighlightingKind::InactiveCode) { + Iter = AddedLine.Tokens.erase(Iter); + AddedLine.IsInactive = true; + } else { + ++Iter; + } + } + } } return DiffedLines; @@ -435,7 +471,7 @@ write16be(static_cast(Token.Kind), OS); } - Lines.push_back({Line.Line, encodeBase64(LineByteTokens)}); + Lines.push_back({Line.Line, encodeBase64(LineByteTokens), Line.IsInactive}); } return Lines; @@ -476,6 +512,8 @@ return "storage.type.primitive.cpp"; case HighlightingKind::Macro: return "entity.name.function.preprocessor.cpp"; + case HighlightingKind::InactiveCode: + return "meta.disabled"; } llvm_unreachable("unhandled HighlightingKind"); } diff --git a/clang-tools-extra/clangd/test/semantic-highlighting.test b/clang-tools-extra/clangd/test/semantic-highlighting.test --- a/clang-tools-extra/clangd/test/semantic-highlighting.test +++ b/clang-tools-extra/clangd/test/semantic-highlighting.test @@ -50,6 +50,9 @@ # CHECK-NEXT: "storage.type.primitive.cpp" # CHECK-NEXT: ], # CHECK-NEXT: [ +# CHECK-NEXT: "meta.disabled" +# CHECK-NEXT: ], +# CHECK-NEXT: [ # CHECK-NEXT: "entity.name.function.preprocessor.cpp" # CHECK-NEXT: ] # CHECK-NEXT: ]