Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -24,13 +24,37 @@ namespace clangd { struct SymbolLocation { + // Specify a position (Line, Column) of symbol. Using Line/Column allows us to + // build LSP responses without reading the file content. + // + // (Line, Column) is encoded as a 32-bits integer where 20 bits for line + // (4M) and 12 bits for column (4K), which is sufficient for most + // human-readable source code. + struct Position { + static constexpr int LineBits = 20; + static constexpr int ColumnBits = 12; + + uint32_t Line : LineBits; // 0-based + uint32_t Column: ColumnBits; // 0-based + + bool operator==(const Position& Pos) const { + return Line == Pos.Line && Column == Pos.Column; + } + }; + // The URI of the source file where a symbol occurs. llvm::StringRef FileURI; // The 0-based offsets of the symbol from the beginning of the source file, // using half-open range, [StartOffset, EndOffset). + // DO NOT use these fields, as they will be removed immediately. + // FIXME(hokein): remove these fields in favor of Position. unsigned StartOffset = 0; unsigned EndOffset = 0; + /// The symbol range, using half-open range [Start, End). + Position Start; + Position End; + operator bool() const { return !FileURI.empty(); } }; llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolLocation &); Index: clangd/index/Index.cpp =================================================================== --- clangd/index/Index.cpp +++ clangd/index/Index.cpp @@ -19,7 +19,8 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolLocation &L) { if (!L) return OS << "(none)"; - return OS << L.FileURI << "[" << L.StartOffset << "-" << L.EndOffset << ")"; + return OS << L.FileURI << "[" << L.Start.Line << ":" << L.Start.Column << "-" + << L.End.Line << ":" << L.End.Column << ")"; } SymbolID::SymbolID(StringRef USR) Index: clangd/index/SymbolCollector.cpp =================================================================== --- clangd/index/SymbolCollector.cpp +++ clangd/index/SymbolCollector.cpp @@ -192,9 +192,23 @@ FileURIStorage = std::move(*U); SymbolLocation Result; Result.FileURI = FileURIStorage; - Result.StartOffset = SM.getFileOffset(NameLoc); - Result.EndOffset = Result.StartOffset + clang::Lexer::MeasureTokenLength( - NameLoc, SM, LangOpts); + auto TokenLength = clang::Lexer::MeasureTokenLength(NameLoc, SM, LangOpts); + + auto CreatePosition = [&SM](SourceLocation Loc) { + auto FileIdAndOffset = SM.getDecomposedLoc(Loc); + auto FileId = FileIdAndOffset.first; + auto Offset = FileIdAndOffset.second; + SymbolLocation::Position Pos; + // Position is 0-based while source location is 1-based. + Pos.Line = SM.getLineNumber(FileId, Offset) - 1; + Pos.Column= SM.getColumnNumber(FileId, Offset) - 1; + return Pos; + }; + + Result.Start = CreatePosition(NameLoc); + auto EndLoc = NameLoc.getLocWithOffset(TokenLength); + Result.End = CreatePosition(EndLoc); + return std::move(Result); } Index: clangd/index/SymbolYAML.cpp =================================================================== --- clangd/index/SymbolYAML.cpp +++ clangd/index/SymbolYAML.cpp @@ -43,11 +43,36 @@ std::string HexString; }; +struct NormalizedPosition { + using Position = clang::clangd::SymbolLocation::Position; + NormalizedPosition(IO &) {} + NormalizedPosition(IO &, const Position& Pos) { + static_assert(sizeof(Position) == sizeof(uint32_t), + "SymbolLocation::Position structure can not fit into a uint32_t."); + value = (Pos.Line << Position::ColumnBits) + Pos.Column; + } + + Position denormalize(IO&) { + Position Pos; + Pos.Line = value >> Position::ColumnBits; + Pos.Column = value & ((1 << Position::ColumnBits) - 1); + return Pos; + } + + // Encode the SymbolLocation::Position: + // | Line | Column | + uint32_t value; +}; + template <> struct MappingTraits { static void mapping(IO &IO, SymbolLocation &Value) { - IO.mapRequired("StartOffset", Value.StartOffset); - IO.mapRequired("EndOffset", Value.EndOffset); IO.mapRequired("FileURI", Value.FileURI); + MappingNormalization NStart( + IO, Value.Start); + IO.mapRequired("Start", NStart->value); + MappingNormalization NEnd( + IO, Value.End); + IO.mapRequired("End", NEnd->value); } }; Index: unittests/clangd/Annotations.h =================================================================== --- unittests/clangd/Annotations.h +++ unittests/clangd/Annotations.h @@ -58,10 +58,6 @@ // Returns the location of all ranges marked by [[ ]] (or $name[[ ]]). std::vector ranges(llvm::StringRef Name = "") const; - // The same to `range` method, but returns range in offsets [start, end). - std::pair - offsetRange(llvm::StringRef Name = "") const; - private: std::string Code; llvm::StringMap> Points; Index: unittests/clangd/Annotations.cpp =================================================================== --- unittests/clangd/Annotations.cpp +++ unittests/clangd/Annotations.cpp @@ -83,15 +83,5 @@ return {R.begin(), R.end()}; } -std::pair -Annotations::offsetRange(llvm::StringRef Name) const { - auto R = range(Name); - llvm::Expected Start = positionToOffset(Code, R.start); - llvm::Expected End = positionToOffset(Code, R.end); - assert(Start); - assert(End); - return {*Start, *End}; -} - } // namespace clangd } // namespace clang Index: unittests/clangd/SymbolCollectorTests.cpp =================================================================== --- unittests/clangd/SymbolCollectorTests.cpp +++ unittests/clangd/SymbolCollectorTests.cpp @@ -51,13 +51,24 @@ MATCHER_P(IncludeHeader, P, "") { return arg.Detail && arg.Detail->IncludeHeader == P; } -MATCHER_P(DeclRange, Offsets, "") { - return arg.CanonicalDeclaration.StartOffset == Offsets.first && - arg.CanonicalDeclaration.EndOffset == Offsets.second; -} -MATCHER_P(DefRange, Offsets, "") { - return arg.Definition.StartOffset == Offsets.first && - arg.Definition.EndOffset == Offsets.second; +MATCHER_P(DeclLoc, L, "") { + return arg.CanonicalDeclaration.Start == L.Start && + arg.CanonicalDeclaration.End == L.End; +} +MATCHER_P(DeclRange, Pos, "") { + return std::tie(arg.CanonicalDeclaration.Start.Line, + arg.CanonicalDeclaration.Start.Column, + arg.CanonicalDeclaration.End.Line, + arg.CanonicalDeclaration.End.Column) == + std::tie(Pos.start.line, Pos.start.character, Pos.end.line, + Pos.end.character); +} +MATCHER_P(DefRange, Pos, "") { + return std::tie(arg.Definition.Start.Line, + arg.Definition.Start.Column, arg.Definition.End.Line, + arg.Definition.End.Column) == + std::tie(Pos.start.line, Pos.start.character, Pos.end.line, + Pos.end.character); } MATCHER_P(Refs, R, "") { return int(arg.References) == R; } @@ -209,7 +220,7 @@ )"); runSymbolCollector(Header.code(), /*Main=*/""); EXPECT_THAT(Symbols, UnorderedElementsAreArray({AllOf( - QName("Tmpl"), DeclRange(Header.offsetRange()))})); + QName("Tmpl"), DeclRange(Header.range()))})); } TEST_F(SymbolCollectorTest, Locations) { @@ -221,6 +232,9 @@ // Declared in header, defined nowhere. extern int $zdecl[[Z]]; + + void $foodecl[[fo\ +o]](); )cpp"); Annotations Main(R"cpp( int $xdef[[X]] = 42; @@ -234,13 +248,15 @@ EXPECT_THAT( Symbols, UnorderedElementsAre( - AllOf(QName("X"), DeclRange(Header.offsetRange("xdecl")), - DefRange(Main.offsetRange("xdef"))), - AllOf(QName("Cls"), DeclRange(Header.offsetRange("clsdecl")), - DefRange(Main.offsetRange("clsdef"))), - AllOf(QName("print"), DeclRange(Header.offsetRange("printdecl")), - DefRange(Main.offsetRange("printdef"))), - AllOf(QName("Z"), DeclRange(Header.offsetRange("zdecl"))))); + AllOf(QName("X"), DeclRange(Header.range("xdecl")), + DefRange(Main.range("xdef"))), + AllOf(QName("Cls"), DeclRange(Header.range("clsdecl")), + DefRange(Main.range("clsdef"))), + AllOf(QName("print"), DeclRange(Header.range("printdecl")), + DefRange(Main.range("printdef"))), + AllOf(QName("Z"), DeclRange(Header.range("zdecl"))), + AllOf(QName("foo"), DeclRange(Header.range("foodecl"))) + )); } TEST_F(SymbolCollectorTest, References) { @@ -365,9 +381,9 @@ EXPECT_THAT( Symbols, UnorderedElementsAre( - AllOf(QName("abc_Test"), DeclRange(Header.offsetRange("expansion")), + AllOf(QName("abc_Test"), DeclRange(Header.range("expansion")), DeclURI(TestHeaderURI)), - AllOf(QName("Test"), DeclRange(Header.offsetRange("spelling")), + AllOf(QName("Test"), DeclRange(Header.range("spelling")), DeclURI(TestHeaderURI)))); } @@ -382,7 +398,8 @@ /*ExtraArgs=*/{"-DNAME=name"}); EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf( - QName("name"), DeclRange(Header.offsetRange("expansion")), + QName("name"), + DeclRange(Header.range("expansion")), DeclURI(TestHeaderURI)))); } @@ -511,9 +528,9 @@ Kind: Function Lang: Cpp CanonicalDeclaration: - StartOffset: 0 - EndOffset: 1 FileURI: file:///path/foo.h + Start: 4096 # Line 1, column 0 + End: 4097 # Line 1, column 1 CompletionLabel: 'Foo1-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -531,9 +548,9 @@ Kind: Function Lang: Cpp CanonicalDeclaration: - StartOffset: 10 - EndOffset: 12 FileURI: file:///path/bar.h + Start: 4096 # Line 1, column 0 + End: 4097 # Line 1, column 1 CompletionLabel: 'Foo2-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -541,15 +558,22 @@ ... )"; + SymbolLocation ExpectedRange; + ExpectedRange.Start = {1, 0}; + ExpectedRange.End = {1, 1}; + auto Symbols1 = SymbolsFromYAML(YAML1); + EXPECT_THAT(Symbols1, UnorderedElementsAre(AllOf( QName("clang::Foo1"), Labeled("Foo1-label"), Doc("Foo doc"), - Detail("int"), DeclURI("file:///path/foo.h")))); + Detail("int"), DeclURI("file:///path/foo.h"), + DeclLoc(ExpectedRange)))); auto Symbols2 = SymbolsFromYAML(YAML2); EXPECT_THAT(Symbols2, UnorderedElementsAre(AllOf( QName("clang::Foo2"), Labeled("Foo2-label"), - Not(HasDetail()), DeclURI("file:///path/bar.h")))); + Not(HasDetail()), DeclURI("file:///path/bar.h"), + DeclLoc(ExpectedRange)))); std::string ConcatenatedYAML; { @@ -646,17 +670,17 @@ EXPECT_THAT(Symbols, UnorderedElementsAre( AllOf(QName("C"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("cdecl")), + DeclRange(Header.range("cdecl")), IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("cdecl"))), + DefRange(Header.range("cdecl"))), AllOf(QName("S"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("sdecl")), + DeclRange(Header.range("sdecl")), IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("sdecl"))), + DefRange(Header.range("sdecl"))), AllOf(QName("U"), DeclURI(TestHeaderURI), - DeclRange(Header.offsetRange("udecl")), + DeclRange(Header.range("udecl")), IncludeHeader(TestHeaderURI), DefURI(TestHeaderURI), - DefRange(Header.offsetRange("udecl"))))); + DefRange(Header.range("udecl"))))); } TEST_F(SymbolCollectorTest, ClassForwardDeclarationIsCanonical) {