Index: clangd/index/Index.h =================================================================== --- clangd/index/Index.h +++ clangd/index/Index.h @@ -24,13 +24,27 @@ 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. + struct Position { + uint32_t Line = 0; // 0-based + // Using UTF-16 code units. + uint32_t Column = 0; // 0-based + }; + // 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,24 @@ 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 SourceManager is 1-based. + Pos.Line = SM.getLineNumber(FileId, Offset) - 1; + // FIXME: Use UTF-16 code units, not UTF-8 bytes. + 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,18 @@ std::string HexString; }; +template <> struct MappingTraits { + static void mapping(IO &IO, SymbolLocation::Position &Value) { + IO.mapRequired("Line", Value.Line); + IO.mapRequired("Column", Value.Column); + } +}; + 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); + IO.mapRequired("Start", Value.Start); + IO.mapRequired("End", Value.End); } }; 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,20 @@ 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(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 +216,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 +228,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 +244,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 +377,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 +394,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 +524,13 @@ Kind: Function Lang: Cpp CanonicalDeclaration: - StartOffset: 0 - EndOffset: 1 FileURI: file:///path/foo.h + Start: + Line: 1 + Column: 0 + End: + Line: 1 + Column: 1 CompletionLabel: 'Foo1-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -531,9 +548,13 @@ Kind: Function Lang: Cpp CanonicalDeclaration: - StartOffset: 10 - EndOffset: 12 FileURI: file:///path/bar.h + Start: + Line: 1 + Column: 0 + End: + Line: 1 + Column: 1 CompletionLabel: 'Foo2-label' CompletionFilterText: 'filter' CompletionPlainInsertText: 'plain' @@ -542,6 +563,7 @@ )"; auto Symbols1 = SymbolsFromYAML(YAML1); + EXPECT_THAT(Symbols1, UnorderedElementsAre(AllOf( QName("clang::Foo1"), Labeled("Foo1-label"), Doc("Foo doc"), @@ -646,17 +668,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) {