[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
This revision was automatically updated to reflect the committed changes. Closed by commit rL339543: [clangd] Support textEdit in addition to insertText. (authored by kadircet, committed by ). Herald added a subscriber: llvm-commits. Repository: rL LLVM https://reviews.llvm.org/D50449 Files: clang-tools-extra/trunk/clangd/CodeComplete.cpp clang-tools-extra/trunk/clangd/CodeComplete.h clang-tools-extra/trunk/clangd/SourceCode.cpp clang-tools-extra/trunk/clangd/SourceCode.h clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp Index: clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp === --- clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/SourceCodeTests.cpp @@ -37,6 +37,13 @@ return Pos; } +Range range(const std::pair p1, const std::pair p2) { + Range range; + range.start = position(p1.first, p1.second); + range.end = position(p2.first, p2.second); + return range; +} + TEST(SourceCodeTests, PositionToOffset) { // line out of bounds EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed()); @@ -119,6 +126,14 @@ EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; } +TEST(SourceCodeTests, IsRangeConsecutive) { + EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5}))); +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp === --- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp +++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp @@ -1439,6 +1439,82 @@ } } +TEST(CompletionTest, RenderWithFixItMerged) { + TextEdit FixIt; + FixIt.range.end.character = 5; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "->Foo::x"); + EXPECT_TRUE(R.additionalTextEdits.empty()); +} + +TEST(CompletionTest, RenderWithFixItNonMerged) { + TextEdit FixIt; + FixIt.range.end.character = 4; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt)); +} + +TEST(CompletionTest, CompletionTokenRange) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + constexpr const char *TestCodes[] = { + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[Aux]]^; +} + )cpp", + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[]]^; +} + )cpp"}; + for (const auto : TestCodes) { +Annotations TestCode(Text); +auto Results = completions(Server, TestCode.code(), TestCode.point()); + +EXPECT_EQ(Results.Completions.size(), 1u); +EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range()); + } +} + } // namespace } // namespace clangd } // namespace clang Index: clang-tools-extra/trunk/clangd/CodeComplete.h === --- clang-tools-extra/trunk/clangd/CodeComplete.h +++ clang-tools-extra/trunk/clangd/CodeComplete.h @@ -123,6 +123,9 @@ /// converting '->' to '.' on member access. std::vector FixIts; + /// Holds the range of the token we are going to replace with this completion. + Range CompletionTokenRange; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. Index: clang-tools-extra/trunk/clangd/SourceCode.cpp === --- clang-tools-extra/trunk/clangd/SourceCode.cpp +++ clang-tools-extra/trunk/clangd/SourceCode.cpp @@ -224,5 +224,10 @@ return Result; } +bool IsRangeConsecutive(const Range , const Range ) { + return Left.end.line == Right.start.line && + Left.end.character == Right.start.character; +} + } //
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
kadircet updated this revision to Diff 160303. kadircet added a comment. - Rebase. - Resolve discussions. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 Files: clangd/CodeComplete.cpp clangd/CodeComplete.h clangd/SourceCode.cpp clangd/SourceCode.h unittests/clangd/CodeCompleteTests.cpp unittests/clangd/SourceCodeTests.cpp Index: unittests/clangd/SourceCodeTests.cpp === --- unittests/clangd/SourceCodeTests.cpp +++ unittests/clangd/SourceCodeTests.cpp @@ -37,6 +37,13 @@ return Pos; } +Range range(const std::pair p1, const std::pair p2) { + Range range; + range.start = position(p1.first, p1.second); + range.end = position(p2.first, p2.second); + return range; +} + TEST(SourceCodeTests, PositionToOffset) { // line out of bounds EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed()); @@ -119,6 +126,14 @@ EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; } +TEST(SourceCodeTests, IsRangeConsecutive) { + EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5}))); +} + } // namespace } // namespace clangd } // namespace clang Index: unittests/clangd/CodeCompleteTests.cpp === --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -1439,6 +1439,82 @@ } } +TEST(CompletionTest, RenderWithFixItMerged) { + TextEdit FixIt; + FixIt.range.end.character = 5; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "->Foo::x"); + EXPECT_TRUE(R.additionalTextEdits.empty()); +} + +TEST(CompletionTest, RenderWithFixItNonMerged) { + TextEdit FixIt; + FixIt.range.end.character = 4; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt)); +} + +TEST(CompletionTest, CompletionTokenRange) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + constexpr const char *TestCodes[] = { + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[Aux]]^; +} + )cpp", + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[]]^; +} + )cpp"}; + for (const auto : TestCodes) { +Annotations TestCode(Text); +auto Results = completions(Server, TestCode.code(), TestCode.point()); + +EXPECT_EQ(Results.Completions.size(), 1u); +EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range()); + } +} + } // namespace } // namespace clangd } // namespace clang Index: clangd/SourceCode.h === --- clangd/SourceCode.h +++ clangd/SourceCode.h @@ -76,6 +76,8 @@ /// are normalized as much as possible. llvm::Optional getRealPath(const FileEntry *F, const SourceManager ); + +bool IsRangeConsecutive(const Range , const Range ); } // namespace clangd } // namespace clang #endif Index: clangd/SourceCode.cpp === --- clangd/SourceCode.cpp +++ clangd/SourceCode.cpp @@ -224,5 +224,10 @@ return Result; } +bool IsRangeConsecutive(const Range , const Range ) { + return Left.end.line == Right.start.line && + Left.end.character == Right.start.character; +} + } // namespace clangd } // namespace clang Index: clangd/CodeComplete.h === --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -123,6 +123,9 @@ /// converting '->' to '.' on member access. std::vector FixIts; + /// Holds the range of the token we are going to replace with this completion. + Range CompletionTokenRange; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. Index: clangd/CodeComplete.cpp
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
ilya-biryukov accepted this revision. ilya-biryukov added a comment. This revision is now accepted and ready to land. LGTM. Thanks for the change! Could we add an option to clangd to switch it on? (VSCode does not work, but our hacked-up ycm integration seems to work, right?) Comment at: clangd/CodeComplete.cpp:1310 + // other. + for (const auto : FixIts) { +if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) { kadircet wrote: > ilya-biryukov wrote: > > Maybe keep the `reserve` call? (we could reserve one extra element, but > > that's fine) > Actually we could have much more than one extra element, not for the current > situation but may be in the future when we have more fixit completions. We can't have more than one edit adjacent to the completion identifier, right? Otherwise they'll conflict. It is possible to have multiple edits which are consectutive and the last of them is adjacent to the completion identifier, though. But I don't think we handle those cases here anyway. But I'm not too worried about leaving out the reserve call either. At the very worst we could waste some memory on a single completion item, but we shouldn't keep too many around at the same time anyway, so feel free to ignore this one. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
kadircet added inline comments. Comment at: clangd/CodeComplete.cpp:1310 + // other. + for (const auto : FixIts) { +if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) { ilya-biryukov wrote: > Maybe keep the `reserve` call? (we could reserve one extra element, but > that's fine) Actually we could have much more than one extra element, not for the current situation but may be in the future when we have more fixit completions. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
kadircet updated this revision to Diff 160073. kadircet marked 5 inline comments as done. kadircet added a comment. Herald added a subscriber: mgrang. - Resolve discussions. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 Files: clangd/CodeComplete.cpp clangd/CodeComplete.h clangd/SourceCode.cpp clangd/SourceCode.h unittests/clangd/CodeCompleteTests.cpp unittests/clangd/SourceCodeTests.cpp Index: unittests/clangd/SourceCodeTests.cpp === --- unittests/clangd/SourceCodeTests.cpp +++ unittests/clangd/SourceCodeTests.cpp @@ -37,6 +37,13 @@ return Pos; } +Range range(const std::pair p1, const std::pair p2) { + Range range; + range.start = position(p1.first, p1.second); + range.end = position(p2.first, p2.second); + return range; +} + TEST(SourceCodeTests, PositionToOffset) { // line out of bounds EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed()); @@ -119,6 +126,14 @@ EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; } +TEST(SourceCodeTests, IsRangeConsecutive) { + EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5}))); +} + } // namespace } // namespace clangd } // namespace clang Index: unittests/clangd/CodeCompleteTests.cpp === --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -1438,6 +1438,82 @@ } } +TEST(CompletionTest, RenderWithFixItMerged) { + TextEdit FixIt; + FixIt.range.end.character = 5; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "->Foo::x"); + EXPECT_TRUE(R.additionalTextEdits.empty()); +} + +TEST(CompletionTest, RenderWithFixItNonMerged) { + TextEdit FixIt; + FixIt.range.end.character = 4; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt)); +} + +TEST(CompletionTest, CompletionTokenRange) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + constexpr const char *TestCodes[] = { + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[Aux]]^; +} + )cpp", + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[]]^; +} + )cpp"}; + for (const auto : TestCodes) { +Annotations TestCode(Text); +auto Results = completions(Server, TestCode.code(), TestCode.point()); + +EXPECT_EQ(Results.Completions.size(), 1u); +EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range()); + } +} + } // namespace } // namespace clangd } // namespace clang Index: clangd/SourceCode.h === --- clangd/SourceCode.h +++ clangd/SourceCode.h @@ -69,6 +69,7 @@ TextEdit toTextEdit(const FixItHint , const SourceManager , const LangOptions ); +bool IsRangeConsecutive(const Range , const Range ); } // namespace clangd } // namespace clang #endif Index: clangd/SourceCode.cpp === --- clangd/SourceCode.cpp +++ clangd/SourceCode.cpp @@ -208,5 +208,10 @@ return Result; } +bool IsRangeConsecutive(const Range , const Range ) { + return Left.end.line == Right.start.line && + Left.end.character == Right.start.character; +} + } // namespace clangd } // namespace clang Index: clangd/CodeComplete.h === --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -123,6 +123,9 @@ /// converting '->' to '.' on member access. std::vector FixIts; + /// Holds the range of the token we are going to replace with this completion. + Range CompletionTokenRange; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. Index: clangd/CodeComplete.cpp
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
ilya-biryukov added inline comments. Comment at: clangd/CodeComplete.cpp:289 } + std::stable_sort(Completion.FixIts.begin(), Completion.FixIts.end(), + [](const TextEdit , const TextEdit ) { We shouldn't have duplicate/overlapping fix-its, right? Maybe use `std::sort`? Comment at: clangd/CodeComplete.cpp:291 + [](const TextEdit , const TextEdit ) { + return X.range.start.line < Y.range.start.line || +X.range.start.character < use built-in tuples comparison `std::tie(X.line, X.column) < std::tie(Y.line, Y.column)`? Comment at: clangd/CodeComplete.cpp:1057 +Range TextEditRange; +if (CodeCompletionRange.isValid()) { + TextEditRange = halfOpenToRange(Recorder->CCSema->getSourceManager(), Maybe add a comment describing the cases where `isValid()` is false? Does it happen when we complete with empty identifier? Comment at: clangd/CodeComplete.cpp:1310 + // other. + for (const auto : FixIts) { +if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) { Maybe keep the `reserve` call? (we could reserve one extra element, but that's fine) Comment at: clangd/CodeComplete.h:126 + TextEdit textEdit; + Could we avoid adding `textEdit` here? It's an intermediate representation that is only used in clangd, and we should be able materialize the actual `textEdit` when converting to LSP. Comment at: unittests/clangd/SourceCodeTests.cpp:40 +Range range(const std::pair p1, +const std::pair p2) { we convert those `uint64_t` into `int` when setting the positions anyway. Maybe use `int` here too? Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50449: [clangd] Support textEdit in addition to insertText.
kadircet created this revision. kadircet added a reviewer: ilya-biryukov. Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay, ioeric. Completion replies contains textEdits as well. Note that this change relies on https://reviews.llvm.org/D50443. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D50449 Files: clangd/CodeComplete.cpp clangd/CodeComplete.h clangd/SourceCode.cpp clangd/SourceCode.h unittests/clangd/CodeCompleteTests.cpp unittests/clangd/SourceCodeTests.cpp Index: unittests/clangd/SourceCodeTests.cpp === --- unittests/clangd/SourceCodeTests.cpp +++ unittests/clangd/SourceCodeTests.cpp @@ -37,6 +37,14 @@ return Pos; } +Range range(const std::pair p1, +const std::pair p2) { + Range range; + range.start = position(p1.first, p1.second); + range.end = position(p2.first, p2.second); + return range; +} + TEST(SourceCodeTests, PositionToOffset) { // line out of bounds EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed()); @@ -119,6 +127,14 @@ EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; } +TEST(SourceCodeTests, IsRangeConsecutive) { + EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5}))); +} + } // namespace } // namespace clangd } // namespace clang Index: unittests/clangd/CodeCompleteTests.cpp === --- unittests/clangd/CodeCompleteTests.cpp +++ unittests/clangd/CodeCompleteTests.cpp @@ -1438,6 +1438,82 @@ } } +TEST(CompletionTest, RenderWithFixItMerged) { + TextEdit FixIt; + FixIt.range.end.character = 5; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.textEdit.range.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "->Foo::x"); + EXPECT_TRUE(R.additionalTextEdits.empty()); +} + +TEST(CompletionTest, RenderWithFixItNonMerged) { + TextEdit FixIt; + FixIt.range.end.character = 4; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.textEdit.range.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt)); +} + +TEST(CompletionTest, CompletionTokenRange) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + constexpr const char *TestCodes[] = { + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[Aux]]^; +} + )cpp", + R"cpp( +class Auxilary { + public: + void AuxFunction(); +}; +void f() { + Auxilary x; + x.[[]]^; +} + )cpp"}; + for (const auto : TestCodes) { +Annotations TestCode(Text); +auto Results = completions(Server, TestCode.code(), TestCode.point()); + +EXPECT_EQ(Results.Completions.size(), 1u); +EXPECT_THAT(Results.Completions.front().textEdit.range, TestCode.range()); + } +} + } // namespace } // namespace clangd } // namespace clang Index: clangd/SourceCode.h === --- clangd/SourceCode.h +++ clangd/SourceCode.h @@ -69,6 +69,7 @@ TextEdit toTextEdit(const FixItHint , const SourceManager , const LangOptions ); +bool IsRangeConsecutive(const Range , const Range ); } // namespace clangd } // namespace clang #endif Index: clangd/SourceCode.cpp === --- clangd/SourceCode.cpp +++ clangd/SourceCode.cpp @@ -208,5 +208,10 @@ return Result; } +bool IsRangeConsecutive(const Range , const Range ) { + return Left.end.line == Right.start.line && + Left.end.character == Right.start.character; +} + } // namespace clangd } // namespace clang Index: clangd/CodeComplete.h === --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -123,6 +123,8 @@ /// converting '->' to '.' on member access. std::vector FixIts; + TextEdit textEdit; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. Index: clangd/CodeComplete.cpp